about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_abi/src/layout.rs5
-rw-r--r--compiler/rustc_abi/src/lib.rs5
-rw-r--r--compiler/rustc_ast/src/ast.rs5
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs16
-rw-r--r--compiler/rustc_ast/src/visit.rs6
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs2
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs72
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs20
-rw-r--r--compiler/rustc_builtin_macros/src/env.rs2
-rw-r--r--compiler/rustc_codegen_llvm/messages.ftl8
-rw-r--r--compiler/rustc_codegen_llvm/src/consts.rs10
-rw-r--r--compiler/rustc_codegen_llvm/src/errors.rs7
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs1
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs47
-rw-r--r--compiler/rustc_codegen_ssa/src/target_features.rs9
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/backend.rs6
-rw-r--r--compiler/rustc_data_structures/Cargo.toml3
-rw-r--r--compiler/rustc_data_structures/src/marker.rs13
-rw-r--r--compiler/rustc_data_structures/src/sync.rs10
-rw-r--r--compiler/rustc_data_structures/src/sync/parallel.rs13
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs12
-rw-r--r--compiler/rustc_driver_impl/src/pretty.rs2
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0795.md4
-rw-r--r--compiler/rustc_errors/src/emitter.rs6
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_hir/src/hir.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs2
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs77
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/method/mod.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs345
-rw-r--r--compiler/rustc_hir_typeck/src/op.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs12
-rw-r--r--compiler/rustc_incremental/messages.ftl2
-rw-r--r--compiler/rustc_incremental/src/errors.rs6
-rw-r--r--compiler/rustc_incremental/src/persist/load.rs11
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs20
-rw-r--r--compiler/rustc_interface/src/tests.rs12
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h1
-rw-r--r--compiler/rustc_macros/src/serialize.rs30
-rw-r--r--compiler/rustc_metadata/src/lib.rs1
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs31
-rw-r--r--compiler/rustc_middle/src/infer/canonical.rs2
-rw-r--r--compiler/rustc_middle/src/query/erase.rs22
-rw-r--r--compiler/rustc_middle/src/thir/visit.rs3
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs26
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs2
-rw-r--r--compiler/rustc_middle/src/ty/context.rs2
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs2
-rw-r--r--compiler/rustc_middle/src/ty/erase_regions.rs4
-rw-r--r--compiler/rustc_middle/src/ty/flags.rs26
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs4
-rw-r--r--compiler/rustc_middle/src/ty/visit.rs14
-rw-r--r--compiler/rustc_mir_build/messages.ftl8
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs88
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs88
-rw-r--r--compiler/rustc_mir_build/src/errors.rs55
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs839
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/usefulness.rs69
-rw-r--r--compiler/rustc_mir_transform/src/check_alignment.rs105
-rw-r--r--compiler/rustc_mir_transform/src/cross_crate_inline.rs10
-rw-r--r--compiler/rustc_mir_transform/src/sroa.rs4
-rw-r--r--compiler/rustc_parse/messages.ftl3
-rw-r--r--compiler/rustc_parse/src/errors.rs28
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs3
-rw-r--r--compiler/rustc_parse/src/parser/item.rs12
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs2
-rw-r--r--compiler/rustc_session/src/code_stats.rs2
-rw-r--r--compiler/rustc_session/src/config.rs24
-rw-r--r--compiler/rustc_session/src/options.rs24
-rw-r--r--compiler/rustc_span/src/symbol.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs31
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs21
-rw-r--r--compiler/rustc_type_ir/src/canonical.rs29
-rw-r--r--compiler/rustc_type_ir/src/const_kind.rs68
-rw-r--r--compiler/rustc_type_ir/src/flags.rs3
-rw-r--r--compiler/rustc_type_ir/src/lib.rs2
-rw-r--r--compiler/rustc_type_ir/src/predicate_kind.rs139
-rw-r--r--compiler/rustc_type_ir/src/region_kind.rs73
-rw-r--r--compiler/rustc_type_ir/src/ty_kind.rs176
87 files changed, 1467 insertions, 1444 deletions
diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs
index 9127e1d06e8..996fd5bbecf 100644
--- a/compiler/rustc_abi/src/layout.rs
+++ b/compiler/rustc_abi/src/layout.rs
@@ -906,9 +906,8 @@ fn univariant<
                 use rand::{seq::SliceRandom, SeedableRng};
                 // `ReprOptions.layout_seed` is a deterministic seed we can use to randomize field
                 // ordering.
-                let mut rng = rand_xoshiro::Xoshiro128StarStar::seed_from_u64(
-                    repr.field_shuffle_seed.as_u64(),
-                );
+                let mut rng =
+                    rand_xoshiro::Xoshiro128StarStar::seed_from_u64(repr.field_shuffle_seed);
 
                 // Shuffle the ordering of the fields.
                 optimizing.shuffle(&mut rng);
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index 8e7aa59ee34..09a87cf8e2f 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -76,15 +76,14 @@ pub struct ReprOptions {
     pub align: Option<Align>,
     pub pack: Option<Align>,
     pub flags: ReprFlags,
-    #[cfg(feature = "randomize")]
     /// The seed to be used for randomizing a type's layout
     ///
-    /// Note: This could technically be a `Hash128` which would
+    /// Note: This could technically be a `u128` which would
     /// be the "most accurate" hash as it'd encompass the item and crate
     /// hash without loss, but it does pay the price of being larger.
     /// Everything's a tradeoff, a 64-bit seed should be sufficient for our
     /// purposes (primarily `-Z randomize-layout`)
-    pub field_shuffle_seed: rustc_data_structures::stable_hasher::Hash64,
+    pub field_shuffle_seed: u64,
 }
 
 impl ReprOptions {
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 146a4db200c..c85ff6f5c44 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -1548,7 +1548,10 @@ pub struct QSelf {
 #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
 pub enum CaptureBy {
     /// `move |x| y + x`.
-    Value,
+    Value {
+        /// The span of the `move` keyword.
+        move_kw: Span,
+    },
     /// `move` keyword was not specified.
     Ref,
 }
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 0634ee970ec..7c0a78253a2 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -302,6 +302,10 @@ pub trait MutVisitor: Sized {
     fn visit_format_args(&mut self, fmt: &mut FormatArgs) {
         noop_visit_format_args(fmt, self)
     }
+
+    fn visit_capture_by(&mut self, capture_by: &mut CaptureBy) {
+        noop_visit_capture_by(capture_by, self)
+    }
 }
 
 /// Use a map-style function (`FnOnce(T) -> T`) to overwrite a `&mut T`. Useful
@@ -1397,7 +1401,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
         }
         ExprKind::Closure(box Closure {
             binder,
-            capture_clause: _,
+            capture_clause,
             constness,
             asyncness,
             movability: _,
@@ -1409,6 +1413,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
             vis.visit_closure_binder(binder);
             visit_constness(constness, vis);
             vis.visit_asyncness(asyncness);
+            vis.visit_capture_by(capture_clause);
             vis.visit_fn_decl(fn_decl);
             vis.visit_expr(body);
             vis.visit_span(fn_decl_span);
@@ -1562,6 +1567,15 @@ pub fn noop_visit_vis<T: MutVisitor>(visibility: &mut Visibility, vis: &mut T) {
     vis.visit_span(&mut visibility.span);
 }
 
+pub fn noop_visit_capture_by<T: MutVisitor>(capture_by: &mut CaptureBy, vis: &mut T) {
+    match capture_by {
+        CaptureBy::Ref => {}
+        CaptureBy::Value { move_kw } => {
+            vis.visit_span(move_kw);
+        }
+    }
+}
+
 /// Some value for the AST node that is valid but possibly meaningless.
 pub trait DummyAstNode {
     fn dummy() -> Self;
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index e091961a144..1caa39e2dd9 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -251,6 +251,9 @@ pub trait Visitor<'ast>: Sized {
     fn visit_inline_asm_sym(&mut self, sym: &'ast InlineAsmSym) {
         walk_inline_asm_sym(self, sym)
     }
+    fn visit_capture_by(&mut self, _capture_by: &'ast CaptureBy) {
+        // Nothing to do
+    }
 }
 
 #[macro_export]
@@ -857,7 +860,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
         }
         ExprKind::Closure(box Closure {
             binder,
-            capture_clause: _,
+            capture_clause,
             asyncness: _,
             constness: _,
             movability: _,
@@ -866,6 +869,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
             fn_decl_span: _,
             fn_arg_span: _,
         }) => {
+            visitor.visit_capture_by(capture_clause);
             visitor.visit_fn(FnKind::Closure(binder, fn_decl, body), expression.span, expression.id)
         }
         ExprKind::Block(block, opt_label) => {
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index c73d2382db8..9a70e6d7c4a 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -1201,7 +1201,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             }
 
             let async_expr = this.make_async_expr(
-                CaptureBy::Value,
+                CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
                 closure_id,
                 None,
                 body.span,
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index e71f421659e..48421ff7140 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -146,37 +146,49 @@ pub fn print_crate<'a>(
     s.s.eof()
 }
 
-/// This makes printed token streams look slightly nicer,
-/// and also addresses some specific regressions described in #63896 and #73345.
-fn space_between(prev: &TokenTree, curr: &TokenTree) -> bool {
-    if let TokenTree::Token(token, _) = prev {
-        // No space after these tokens, e.g. `x.y`, `$e`
-        // (The carets point to `prev`.)       ^     ^
-        if matches!(token.kind, token::Dot | token::Dollar) {
-            return false;
-        }
-        if let token::DocComment(comment_kind, ..) = token.kind {
-            return comment_kind != CommentKind::Line;
-        }
-    }
-    match curr {
-        // No space before these tokens, e.g. `foo,`, `println!`, `x.y`
-        // (The carets point to `curr`.)          ^           ^     ^
+/// Should two consecutive tokens be printed with a space between them?
+///
+/// Note: some old proc macros parse pretty-printed output, so changes here can
+/// break old code. For example:
+/// - #63896: `#[allow(unused,` must be printed rather than `#[allow(unused ,`
+/// - #73345: `#[allow(unused)] must be printed rather than `# [allow(unused)]
+///
+fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool {
+    use token::*;
+    use Delimiter::*;
+    use TokenTree::Delimited as Del;
+    use TokenTree::Token as Tok;
+
+    // Each match arm has one or more examples in comments. The default is to
+    // insert space between adjacent tokens, except for the cases listed in
+    // this match.
+    match (tt1, tt2) {
+        // No space after line doc comments.
+        (Tok(Token { kind: DocComment(CommentKind::Line, ..), .. }, _), _) => false,
+
+        // `.` + ANYTHING: `x.y`, `tup.0`
+        // `$` + ANYTHING: `$e`
+        (Tok(Token { kind: Dot | Dollar, .. }, _), _) => false,
+
+        // ANYTHING + `,`: `foo,`
+        // ANYTHING + `.`: `x.y`, `tup.0`
+        // ANYTHING + `!`: `foo! { ... }`
         //
-        // FIXME: having `Not` here works well for macro invocations like
-        // `println!()`, but is bad when `!` means "logical not" or "the never
-        // type", where the lack of space causes ugliness like this:
-        // `Fn() ->!`, `x =! y`, `if! x { f(); }`.
-        TokenTree::Token(token, _) => !matches!(token.kind, token::Comma | token::Not | token::Dot),
-        // No space before parentheses if preceded by these tokens, e.g. `foo(...)`
-        TokenTree::Delimited(_, Delimiter::Parenthesis, _) => {
-            !matches!(prev, TokenTree::Token(Token { kind: token::Ident(..), .. }, _))
-        }
-        // No space before brackets if preceded by these tokens, e.g. `#[...]`
-        TokenTree::Delimited(_, Delimiter::Bracket, _) => {
-            !matches!(prev, TokenTree::Token(Token { kind: token::Pound, .. }, _))
-        }
-        TokenTree::Delimited(..) => true,
+        // FIXME: Incorrect cases:
+        // - Logical not: `x =! y`, `if! x { f(); }`
+        // - Never type: `Fn() ->!`
+        (_, Tok(Token { kind: Comma | Dot | Not, .. }, _)) => false,
+
+        // IDENT + `(`: `f(3)`
+        //
+        // FIXME: Incorrect cases:
+        // - Let: `let(a, b) = (1, 2)`
+        (Tok(Token { kind: Ident(..), .. }, _), Del(_, Parenthesis, _)) => false,
+
+        // `#` + `[`: `#[attr]`
+        (Tok(Token { kind: Pound, .. }, _), Del(_, Bracket, _)) => false,
+
+        _ => true,
     }
 }
 
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index e84af12d3f9..edbc3500373 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -673,7 +673,7 @@ impl<'a> State<'a> {
 
     fn print_capture_clause(&mut self, capture_clause: ast::CaptureBy) {
         match capture_clause {
-            ast::CaptureBy::Value => self.word_space("move"),
+            ast::CaptureBy::Value { .. } => self.word_space("move"),
             ast::CaptureBy::Ref => {}
         }
     }
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index 695ac6980cd..41d6b98d7cf 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -2,7 +2,7 @@ use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed
 use rustc_middle::mir::*;
 use rustc_middle::ty::{self, Ty};
 use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
-use rustc_span::{BytePos, Span};
+use rustc_span::{BytePos, ExpnKind, MacroKind, Span};
 
 use crate::diagnostics::CapturedMessageOpt;
 use crate::diagnostics::{DescribePlaceOpt, UseSpans};
@@ -488,6 +488,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                         args_span,
                     }
                 });
+
+                self.add_note_for_packed_struct_derive(err, original_path.local);
             }
         }
     }
@@ -594,4 +596,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             );
         }
     }
+
+    /// Adds an explanatory note if the move error occurs in a derive macro
+    /// expansion of a packed struct.
+    /// Such errors happen because derive macro expansions shy away from taking
+    /// references to the struct's fields since doing so would be undefined behaviour
+    fn add_note_for_packed_struct_derive(&self, err: &mut Diagnostic, local: Local) {
+        let local_place: PlaceRef<'tcx> = local.into();
+        let local_ty = local_place.ty(self.body.local_decls(), self.infcx.tcx).ty.peel_refs();
+
+        if let Some(adt) = local_ty.ty_adt_def()
+            && adt.repr().packed()
+            && let ExpnKind::Macro(MacroKind::Derive, name) = self.body.span.ctxt().outer_expn_data().kind
+        {
+            err.note(format!("`#[derive({name})]` triggers a move because taking references to the fields of a packed struct is undefined behaviour"));
+        }
+    }
 }
diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs
index 92da0c069e5..8c2fa6ee95f 100644
--- a/compiler/rustc_builtin_macros/src/env.rs
+++ b/compiler/rustc_builtin_macros/src/env.rs
@@ -108,7 +108,7 @@ pub fn expand_env<'cx>(
 
             return DummyResult::any(sp);
         }
-        Some(value) => cx.expr_str(sp, value),
+        Some(value) => cx.expr_str(span, value),
     };
     MacEager::expr(e)
 }
diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl
index c0cfe39f1e0..7a86ddc7556 100644
--- a/compiler/rustc_codegen_llvm/messages.ftl
+++ b/compiler/rustc_codegen_llvm/messages.ftl
@@ -76,8 +76,8 @@ codegen_llvm_target_machine = could not create LLVM TargetMachine for triple: {$
 codegen_llvm_target_machine_with_llvm_err = could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err}
 
 codegen_llvm_unknown_ctarget_feature =
-    unknown feature specified for `-Ctarget-feature`: `{$feature}`
-    .note = it is still passed through to the codegen backend
+    unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}`
+    .note = it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future
     .possible_feature = you might have meant: `{$rust_feature}`
     .consider_filing_feature_request = consider filing a feature request
 
@@ -87,6 +87,10 @@ codegen_llvm_unknown_ctarget_feature_prefix =
 
 codegen_llvm_unknown_debuginfo_compression = unknown debuginfo compression algorithm {$algorithm} - will fall back to uncompressed debuginfo
 
+codegen_llvm_unstable_ctarget_feature =
+    unstable feature specified for `-Ctarget-feature`: `{$feature}`
+    .note = this feature is not stably supported; its behavior can change in the future
+
 codegen_llvm_write_bytecode = failed to write bytecode to {$path}: {$err}
 
 codegen_llvm_write_ir = failed to write LLVM IR to {$path}
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index 73821b1685d..307c1264dc1 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -374,15 +374,7 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
 
             let g = self.get_static(def_id);
 
-            // boolean SSA values are i1, but they have to be stored in i8 slots,
-            // otherwise some LLVM optimization passes don't work as expected
-            let mut val_llty = self.val_ty(v);
-            let v = if val_llty == self.type_i1() {
-                val_llty = self.type_i8();
-                llvm::LLVMConstZExt(v, val_llty)
-            } else {
-                v
-            };
+            let val_llty = self.val_ty(v);
 
             let instance = Instance::mono(self.tcx, def_id);
             let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index 665d195790c..10ca5ad802a 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -26,6 +26,13 @@ pub(crate) struct UnknownCTargetFeature<'a> {
     pub rust_feature: PossibleFeature<'a>,
 }
 
+#[derive(Diagnostic)]
+#[diag(codegen_llvm_unstable_ctarget_feature)]
+#[note]
+pub(crate) struct UnstableCTargetFeature<'a> {
+    pub feature: &'a str,
+}
+
 #[derive(Subdiagnostic)]
 pub(crate) enum PossibleFeature<'a> {
     #[help(codegen_llvm_possible_feature)]
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index a038b3af03d..7fc02a95be0 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -969,7 +969,6 @@ extern "C" {
         ConstantIndices: *const &'a Value,
         NumIndices: c_uint,
     ) -> &'a Value;
-    pub fn LLVMConstZExt<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value;
     pub fn LLVMConstPtrToInt<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value;
     pub fn LLVMConstIntToPtr<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value;
     pub fn LLVMConstBitCast<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value;
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index 7c8ef67ffd1..cc4ccaf19c2 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -1,7 +1,7 @@
 use crate::back::write::create_informational_target_machine;
 use crate::errors::{
     PossibleFeature, TargetFeatureDisableOrEnable, UnknownCTargetFeature,
-    UnknownCTargetFeaturePrefix,
+    UnknownCTargetFeaturePrefix, UnstableCTargetFeature,
 };
 use crate::llvm;
 use libc::c_int;
@@ -531,25 +531,34 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str
             };
 
             let feature = backend_feature_name(s)?;
-            // Warn against use of LLVM specific feature names on the CLI.
-            if diagnostics && !supported_features.iter().any(|&(v, _)| v == feature) {
-                let rust_feature = supported_features.iter().find_map(|&(rust_feature, _)| {
-                    let llvm_features = to_llvm_features(sess, rust_feature);
-                    if llvm_features.contains(&feature) && !llvm_features.contains(&rust_feature) {
-                        Some(rust_feature)
+            // Warn against use of LLVM specific feature names and unstable features on the CLI.
+            if diagnostics {
+                let feature_state = supported_features.iter().find(|&&(v, _)| v == feature);
+                if feature_state.is_none() {
+                    let rust_feature = supported_features.iter().find_map(|&(rust_feature, _)| {
+                        let llvm_features = to_llvm_features(sess, rust_feature);
+                        if llvm_features.contains(&feature)
+                            && !llvm_features.contains(&rust_feature)
+                        {
+                            Some(rust_feature)
+                        } else {
+                            None
+                        }
+                    });
+                    let unknown_feature = if let Some(rust_feature) = rust_feature {
+                        UnknownCTargetFeature {
+                            feature,
+                            rust_feature: PossibleFeature::Some { rust_feature },
+                        }
                     } else {
-                        None
-                    }
-                });
-                let unknown_feature = if let Some(rust_feature) = rust_feature {
-                    UnknownCTargetFeature {
-                        feature,
-                        rust_feature: PossibleFeature::Some { rust_feature },
-                    }
-                } else {
-                    UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
-                };
-                sess.emit_warning(unknown_feature);
+                        UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
+                    };
+                    sess.emit_warning(unknown_feature);
+                } else if feature_state.is_some_and(|(_name, feature_gate)| feature_gate.is_some())
+                {
+                    // An unstable feature. Warn about using it.
+                    sess.emit_warning(UnstableCTargetFeature { feature });
+                }
             }
 
             if diagnostics {
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index 9c053338c85..2936f1de3cb 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -23,6 +23,15 @@ pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"];
 // check whether they're named already elsewhere in rust
 // e.g. in stdarch and whether the given name matches LLVM's
 // if it doesn't, to_llvm_feature in llvm_util in rustc_codegen_llvm needs to be adapted
+//
+// When adding a new feature, be particularly mindful of features that affect function ABIs. Those
+// need to be treated very carefully to avoid introducing unsoundness! This often affects features
+// that enable/disable hardfloat support (see https://github.com/rust-lang/rust/issues/116344 for an
+// example of this going wrong), but features enabling new SIMD registers are also a concern (see
+// https://github.com/rust-lang/rust/issues/116558 for an example of this going wrong).
+//
+// Stabilizing a target feature (setting the 2nd component of the pair to `None`) requires t-lang
+// approval.
 
 const ARM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
     // tidy-alphabetical-start
diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs
index ac8123bc1be..35744d9a167 100644
--- a/compiler/rustc_codegen_ssa/src/traits/backend.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs
@@ -104,11 +104,7 @@ pub trait CodegenBackend {
         outputs: &OutputFilenames,
     ) -> Result<(CodegenResults, FxIndexMap<WorkProductId, WorkProduct>), ErrorGuaranteed>;
 
-    /// This is called on the returned `Box<dyn Any>` from `join_codegen`
-    ///
-    /// # Panics
-    ///
-    /// Panics when the passed `Box<dyn Any>` was not returned by `join_codegen`.
+    /// This is called on the returned `CodegenResults` from `join_codegen`
     fn link(
         &self,
         sess: &Session,
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml
index 2701fdbbd77..8d91c4c4376 100644
--- a/compiler/rustc_data_structures/Cargo.toml
+++ b/compiler/rustc_data_structures/Cargo.toml
@@ -47,6 +47,9 @@ features = [
 memmap2 = "0.2.1"
 # tidy-alphabetical-end
 
+[target.'cfg(any(target_arch = "powerpc", target_arch = "mips"))'.dependencies]
+portable-atomic = "1.5.1"
+
 [features]
 # tidy-alphabetical-start
 rustc_use_parallel_compiler = ["indexmap/rustc-rayon", "rustc-rayon", "rustc-rayon-core"]
diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs
index a8c442377fb..266e54604a6 100644
--- a/compiler/rustc_data_structures/src/marker.rs
+++ b/compiler/rustc_data_structures/src/marker.rs
@@ -138,7 +138,6 @@ cfg_match! {
             [std::sync::atomic::AtomicUsize]
             [std::sync::atomic::AtomicU8]
             [std::sync::atomic::AtomicU32]
-            [std::sync::atomic::AtomicU64]
             [std::backtrace::Backtrace]
             [std::io::Error]
             [std::fs::File]
@@ -148,6 +147,18 @@ cfg_match! {
             [crate::owned_slice::OwnedSlice]
         );
 
+        // PowerPC and MIPS platforms with 32-bit pointers do not
+        // have AtomicU64 type.
+        #[cfg(not(any(target_arch = "powerpc", target_arch = "mips")))]
+        already_sync!(
+            [std::sync::atomic::AtomicU64]
+        );
+
+        #[cfg(any(target_arch = "powerpc", target_arch = "mips"))]
+        already_sync!(
+            [portable_atomic::AtomicU64]
+        );
+
         macro_rules! impl_dyn_sync {
             ($($($attr: meta)* [$ty: ty where $($generics2: tt)*])*) => {
                 $(unsafe impl<$($generics2)*> DynSync for $ty {})*
diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs
index f957734b04d..43221d70e21 100644
--- a/compiler/rustc_data_structures/src/sync.rs
+++ b/compiler/rustc_data_structures/src/sync.rs
@@ -265,7 +265,15 @@ cfg_match! {
 
         pub use std::sync::OnceLock;
 
-        pub use std::sync::atomic::{AtomicBool, AtomicUsize, AtomicU32, AtomicU64};
+        pub use std::sync::atomic::{AtomicBool, AtomicUsize, AtomicU32};
+
+        // PowerPC and MIPS platforms with 32-bit pointers do not
+        // have AtomicU64 type.
+        #[cfg(not(any(target_arch = "powerpc", target_arch = "mips")))]
+        pub use std::sync::atomic::AtomicU64;
+
+        #[cfg(any(target_arch = "powerpc", target_arch = "mips"))]
+        pub use portable_atomic::AtomicU64;
 
         pub use std::sync::Arc as Lrc;
         pub use std::sync::Weak as Weak;
diff --git a/compiler/rustc_data_structures/src/sync/parallel.rs b/compiler/rustc_data_structures/src/sync/parallel.rs
index 39dddb59569..5695d839d3e 100644
--- a/compiler/rustc_data_structures/src/sync/parallel.rs
+++ b/compiler/rustc_data_structures/src/sync/parallel.rs
@@ -77,12 +77,12 @@ mod disabled {
         })
     }
 
-    pub fn try_par_for_each_in<T: IntoIterator, E: Copy>(
+    pub fn try_par_for_each_in<T: IntoIterator, E>(
         t: T,
         mut for_each: impl FnMut(T::Item) -> Result<(), E>,
     ) -> Result<(), E> {
         parallel_guard(|guard| {
-            t.into_iter().fold(Ok(()), |ret, i| guard.run(|| for_each(i)).unwrap_or(ret).and(ret))
+            t.into_iter().filter_map(|i| guard.run(|| for_each(i))).fold(Ok(()), Result::and)
         })
     }
 
@@ -178,7 +178,7 @@ mod enabled {
 
     pub fn try_par_for_each_in<
         T: IntoIterator + IntoParallelIterator<Item = <T as IntoIterator>::Item>,
-        E: Copy + Send,
+        E: Send,
     >(
         t: T,
         for_each: impl Fn(<T as IntoIterator>::Item) -> Result<(), E> + DynSync + DynSend,
@@ -187,11 +187,10 @@ mod enabled {
             if mode::is_dyn_thread_safe() {
                 let for_each = FromDyn::from(for_each);
                 t.into_par_iter()
-                    .fold_with(Ok(()), |ret, i| guard.run(|| for_each(i)).unwrap_or(ret).and(ret))
-                    .reduce(|| Ok(()), |a, b| a.and(b))
+                    .filter_map(|i| guard.run(|| for_each(i)))
+                    .reduce(|| Ok(()), Result::and)
             } else {
-                t.into_iter()
-                    .fold(Ok(()), |ret, i| guard.run(|| for_each(i)).unwrap_or(ret).and(ret))
+                t.into_iter().filter_map(|i| guard.run(|| for_each(i))).fold(Ok(()), Result::and)
             }
         })
     }
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index ee4337754a9..84ae45d6a2b 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -41,6 +41,7 @@ use rustc_session::cstore::MetadataLoader;
 use rustc_session::getopts::{self, Matches};
 use rustc_session::lint::{Lint, LintId};
 use rustc_session::{config, EarlyErrorHandler, Session};
+use rustc_span::def_id::LOCAL_CRATE;
 use rustc_span::source_map::FileLoader;
 use rustc_span::symbol::sym;
 use rustc_span::FileName;
@@ -421,8 +422,12 @@ fn run_compiler(
                     // effects of writing the dep-info and reporting errors.
                     queries.global_ctxt()?.enter(|tcx| tcx.output_filenames(()));
                 } else {
-                    let krate = queries.parse()?.steal();
-                    pretty::print(sess, *ppm, pretty::PrintExtra::AfterParsing { krate });
+                    let krate = queries.parse()?;
+                    pretty::print(
+                        sess,
+                        *ppm,
+                        pretty::PrintExtra::AfterParsing { krate: &*krate.borrow() },
+                    );
                 }
                 trace!("finished pretty-printing");
                 return early_exit();
@@ -477,8 +482,7 @@ fn run_compiler(
             }
 
             if sess.opts.unstable_opts.print_vtable_sizes {
-                let crate_name =
-                    compiler.session().opts.crate_name.as_deref().unwrap_or("<UNKNOWN_CRATE>");
+                let crate_name = queries.global_ctxt()?.enter(|tcx| tcx.crate_name(LOCAL_CRATE));
 
                 sess.code_stats.print_vtable_sizes(crate_name);
             }
diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs
index 8c6fee83013..cc533b9941a 100644
--- a/compiler/rustc_driver_impl/src/pretty.rs
+++ b/compiler/rustc_driver_impl/src/pretty.rs
@@ -217,7 +217,7 @@ fn write_or_print(out: &str, sess: &Session) {
 // Extra data for pretty-printing, the form of which depends on what kind of
 // pretty-printing we are doing.
 pub enum PrintExtra<'tcx> {
-    AfterParsing { krate: ast::Crate },
+    AfterParsing { krate: &'tcx ast::Crate },
     NeedsAstMap { tcx: TyCtxt<'tcx> },
 }
 
diff --git a/compiler/rustc_error_codes/src/error_codes/E0795.md b/compiler/rustc_error_codes/src/error_codes/E0795.md
index 8b4b2dc87fd..20f51441c29 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0795.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0795.md
@@ -3,7 +3,7 @@ Invalid argument for the `offset_of!` macro.
 Erroneous code example:
 
 ```compile_fail,E0795
-#![feature(offset_of)]
+#![feature(offset_of, offset_of_enum)]
 
 let x = std::mem::offset_of!(Option<u8>, Some);
 ```
@@ -16,7 +16,7 @@ The offset of the contained `u8` in the `Option<u8>` can be found by specifying
 the field name `0`:
 
 ```
-#![feature(offset_of)]
+#![feature(offset_of, offset_of_enum)]
 
 let x: usize = std::mem::offset_of!(Option<u8>, Some.0);
 ```
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 6d95fbcfad0..68dba860291 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -2362,11 +2362,7 @@ impl FileWithAnnotatedLines {
 
                 let label = label.as_ref().map(|m| {
                     normalize_whitespace(
-                        &emitter
-                            .translate_message(m, &args)
-                            .map_err(Report::new)
-                            .unwrap()
-                            .to_string(),
+                        &emitter.translate_message(m, &args).map_err(Report::new).unwrap(),
                     )
                 });
 
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 11782e33d84..64b5a7d2921 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -526,6 +526,8 @@ declare_features! (
     /// In that case, `dyn Trait: Trait` does not hold. Moreover, coercions and
     /// casts in safe Rust to `dyn Trait` for such a `Trait` is also forbidden.
     (unstable, object_safe_for_dispatch, "1.40.0", Some(43561), None),
+    /// Allows using enums in offset_of!
+    (unstable, offset_of_enum, "CURRENT_RUSTC_VERSION", Some(106655), None),
     /// Allows using `#[optimize(X)]`.
     (unstable, optimize_attribute, "1.34.0", Some(54882), None),
     /// Allows exhaustive integer pattern matching on `usize` and `isize`.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index d88f165b9e5..c4e44a6a4e3 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -3566,6 +3566,15 @@ impl<'hir> OwnerNode<'hir> {
         }
     }
 
+    pub fn fn_sig(self) -> Option<&'hir FnSig<'hir>> {
+        match self {
+            OwnerNode::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. })
+            | OwnerNode::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. })
+            | OwnerNode::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig),
+            _ => None,
+        }
+    }
+
     pub fn fn_decl(self) -> Option<&'hir FnDecl<'hir>> {
         match self {
             OwnerNode::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. })
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 90babbb63a0..857515f971a 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -2162,7 +2162,7 @@ pub(super) fn check_type_bounds<'tcx>(
     impl_ty: ty::AssocItem,
     impl_trait_ref: ty::TraitRef<'tcx>,
 ) -> Result<(), ErrorGuaranteed> {
-    let param_env = param_env_with_gat_bounds(tcx, impl_ty, impl_trait_ref);
+    let param_env = tcx.param_env(impl_ty.def_id);
     debug!(?param_env);
 
     let container_id = impl_ty.container_id(tcx);
@@ -2217,8 +2217,14 @@ pub(super) fn check_type_bounds<'tcx>(
         .collect();
     debug!("check_type_bounds: item_bounds={:?}", obligations);
 
+    // Normalize predicates with the assumption that the GAT may always normalize
+    // to its definition type. This should be the param-env we use to *prove* the
+    // predicate too, but we don't do that because of performance issues.
+    // See <https://github.com/rust-lang/rust/pull/117542#issue-1976337685>.
+    let normalize_param_env = param_env_with_gat_bounds(tcx, impl_ty, impl_trait_ref);
     for mut obligation in util::elaborate(tcx, obligations) {
-        let normalized_predicate = ocx.normalize(&normalize_cause, param_env, obligation.predicate);
+        let normalized_predicate =
+            ocx.normalize(&normalize_cause, normalize_param_env, obligation.predicate);
         debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
         obligation.predicate = normalized_predicate;
 
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 046983e90f7..eb4491b89bf 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -32,6 +32,7 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _
 use rustc_trait_selection::traits::{
     self, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc,
 };
+use rustc_type_ir::TypeFlags;
 
 use std::cell::LazyCell;
 use std::ops::{ControlFlow, Deref};
@@ -1877,7 +1878,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
                 continue;
             }
             // Match the existing behavior.
-            if pred.is_global() && !pred.has_late_bound_vars() {
+            if pred.is_global() && !pred.has_type_flags(TypeFlags::HAS_BINDER_VARS) {
                 let pred = self.normalize(span, None, pred);
                 let hir_node = tcx.hir().find_by_def_id(self.body_def_id);
 
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index ebb9e6f42d9..6424d1c7931 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -880,7 +880,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                             (pair, r)
                         })
                         .unzip();
-                self.record_late_bound_vars(hir_id, binders.clone());
+                self.record_late_bound_vars(hir_id, binders);
                 // Even if there are no lifetimes defined here, we still wrap it in a binder
                 // scope. If there happens to be a nested poly trait ref (an error), that
                 // will be `Concatenating` anyways, so we don't have to worry about the depth
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 1ce6bb6ca15..5f82d9f06c6 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -2017,7 +2017,7 @@ impl<'a> State<'a> {
 
     fn print_capture_clause(&mut self, capture_clause: hir::CaptureBy) {
         match capture_clause {
-            hir::CaptureBy::Value => self.word_space("move"),
+            hir::CaptureBy::Value { .. } => self.word_space("move"),
             hir::CaptureBy::Ref => {}
         }
     }
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index a11fe10c01c..9f439a2b32a 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -13,7 +13,7 @@ use crate::errors::{
     YieldExprOutsideOfCoroutine,
 };
 use crate::fatally_break_rust;
-use crate::method::{MethodCallComponents, SelfSource};
+use crate::method::SelfSource;
 use crate::type_error_struct;
 use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation};
 use crate::{
@@ -512,7 +512,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) -> Ty<'tcx> {
         let tcx = self.tcx;
         let (res, opt_ty, segs) =
-            self.resolve_ty_and_res_fully_qualified_call(qpath, expr.hir_id, expr.span);
+            self.resolve_ty_and_res_fully_qualified_call(qpath, expr.hir_id, expr.span, Some(args));
         let ty = match res {
             Res::Err => {
                 self.suggest_assoc_method_call(segs);
@@ -959,12 +959,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 Applicability::MachineApplicable,
             );
         });
+        self.check_for_missing_semi(lhs, &mut err);
 
         adjust_err(&mut err);
 
         err.emit();
     }
 
+    /// Check if the expression that could not be assigned to was a typoed expression that
+    pub fn check_for_missing_semi(
+        &self,
+        expr: &'tcx hir::Expr<'tcx>,
+        err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+    ) -> bool {
+        if let hir::ExprKind::Binary(binop, lhs, rhs) = expr.kind
+            && let hir::BinOpKind::Mul = binop.node
+            && self.tcx.sess.source_map().is_multiline(lhs.span.between(rhs.span))
+            && rhs.is_syntactic_place_expr()
+        {
+            //      v missing semicolon here
+            // foo()
+            // *bar = baz;
+            // (#80446).
+            err.span_suggestion_verbose(
+                lhs.span.shrink_to_hi(),
+                "you might have meant to write a semicolon here",
+                ";".to_string(),
+                Applicability::MachineApplicable,
+            );
+            return true;
+        }
+        false
+    }
+
     // Check if an expression `original_expr_id` comes from the condition of a while loop,
     /// as opposed from the body of a while loop, which we can naively check by iterating
     /// parents until we find a loop...
@@ -1305,7 +1332,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         segment.ident,
                         SelfSource::MethodCall(rcvr),
                         error,
-                        Some(MethodCallComponents { receiver: rcvr, args, full_expr: expr }),
+                        Some(args),
                         expected,
                         false,
                     ) {
@@ -1524,21 +1551,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
             _ => {}
         }
-        // If someone calls a const fn, they can extract that call out into a separate constant (or a const
-        // block in the future), so we check that to tell them that in the diagnostic. Does not affect typeck.
-        let is_const_fn = match element.kind {
+        // If someone calls a const fn or constructs a const value, they can extract that
+        // out into a separate constant (or a const block in the future), so we check that
+        // to tell them that in the diagnostic. Does not affect typeck.
+        let is_constable = match element.kind {
             hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() {
-                ty::FnDef(def_id, _) => tcx.is_const_fn(def_id),
-                _ => false,
+                ty::FnDef(def_id, _) if tcx.is_const_fn(def_id) => traits::IsConstable::Fn,
+                _ => traits::IsConstable::No,
             },
-            _ => false,
+            hir::ExprKind::Path(qpath) => {
+                match self.typeck_results.borrow().qpath_res(&qpath, element.hir_id) {
+                    Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => traits::IsConstable::Ctor,
+                    _ => traits::IsConstable::No,
+                }
+            }
+            _ => traits::IsConstable::No,
         };
 
         // If the length is 0, we don't create any elements, so we don't copy any. If the length is 1, we
         // don't copy that one element, we move it. Only check for Copy if the length is larger.
         if count.try_eval_target_usize(tcx, self.param_env).map_or(true, |len| len > 1) {
             let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
-            let code = traits::ObligationCauseCode::RepeatElementCopy { is_const_fn };
+            let code = traits::ObligationCauseCode::RepeatElementCopy {
+                is_constable,
+                elt_type: element_ty,
+                elt_span: element.span,
+                elt_stmt_span: self
+                    .tcx
+                    .hir()
+                    .parent_iter(element.hir_id)
+                    .find_map(|(_, node)| match node {
+                        hir::Node::Item(it) => Some(it.span),
+                        hir::Node::Stmt(stmt) => Some(stmt.span),
+                        _ => None,
+                    })
+                    .expect("array repeat expressions must be inside an item or statement"),
+            };
             self.require_type_meets(element_ty, element.span, code, lang_item);
         }
     }
@@ -3119,6 +3167,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     let (ident, _def_scope) =
                         self.tcx.adjust_ident_and_get_scope(field, container_def.did(), block);
 
+                    if !self.tcx.features().offset_of_enum {
+                        rustc_session::parse::feature_err(
+                            &self.tcx.sess.parse_sess,
+                            sym::offset_of_enum,
+                            ident.span,
+                            "using enums in offset_of is experimental",
+                        ).emit();
+                    }
+
                     let Some((index, variant)) = container_def.variants()
                         .iter_enumerated()
                         .find(|(_, v)| v.ident(self.tcx).normalize_to_macros_2_0() == ident) else {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index b5a07f0d3e9..750ed2c3491 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -797,6 +797,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         qpath: &'tcx QPath<'tcx>,
         hir_id: hir::HirId,
         span: Span,
+        args: Option<&'tcx [hir::Expr<'tcx>]>,
     ) -> (Res, Option<RawTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]) {
         debug!(
             "resolve_ty_and_res_fully_qualified_call: qpath={:?} hir_id={:?} span={:?}",
@@ -898,7 +899,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         item_name,
                         SelfSource::QPath(qself),
                         error,
-                        None,
+                        args,
                         Expectation::NoExpectation,
                         trait_missing_method && span.edition().at_least_rust_2021(), // emits missing method for trait only after edition 2021
                     ) {
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
index 86a0e95de1d..d69d2529b18 100644
--- a/compiler/rustc_hir_typeck/src/method/mod.rs
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -7,7 +7,7 @@ mod prelude2021;
 pub mod probe;
 mod suggest;
 
-pub use self::suggest::{MethodCallComponents, SelfSource};
+pub use self::suggest::SelfSource;
 pub use self::MethodError::*;
 
 use crate::errors::OpMethodGenericParams;
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 12cc5ed2f1a..b1a2df8ace4 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -34,7 +34,7 @@ use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::def_id::DefIdSet;
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::Symbol;
-use rustc_span::{edit_distance, source_map, ExpnKind, FileName, MacroKind, Span};
+use rustc_span::{edit_distance, ExpnKind, FileName, MacroKind, Span};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote;
 use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _;
@@ -50,15 +50,6 @@ use rustc_hir::intravisit::Visitor;
 use std::cmp::{self, Ordering};
 use std::iter;
 
-/// After identifying that `full_expr` is a method call, we use this type to keep the expression's
-/// components readily available to us to point at the right place in diagnostics.
-#[derive(Debug, Clone, Copy)]
-pub struct MethodCallComponents<'tcx> {
-    pub receiver: &'tcx hir::Expr<'tcx>,
-    pub args: &'tcx [hir::Expr<'tcx>],
-    pub full_expr: &'tcx hir::Expr<'tcx>,
-}
-
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
         let tcx = self.tcx;
@@ -124,7 +115,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         item_name: Ident,
         source: SelfSource<'tcx>,
         error: MethodError<'tcx>,
-        args: Option<MethodCallComponents<'tcx>>,
+        args: Option<&'tcx [hir::Expr<'tcx>]>,
         expected: Expectation<'tcx>,
         trait_missing_method: bool,
     ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
@@ -167,6 +158,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.note_candidates_on_method_error(
                     rcvr_ty,
                     item_name,
+                    source,
                     args,
                     span,
                     &mut err,
@@ -266,23 +258,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn suggest_missing_writer(
         &self,
         rcvr_ty: Ty<'tcx>,
-        args: MethodCallComponents<'tcx>,
+        rcvr_expr: &hir::Expr<'tcx>,
     ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
         let (ty_str, _ty_file) = self.tcx.short_ty_string(rcvr_ty);
         let mut err = struct_span_err!(
             self.tcx.sess,
-            args.receiver.span,
+            rcvr_expr.span,
             E0599,
             "cannot write into `{}`",
             ty_str
         );
         err.span_note(
-            args.receiver.span,
+            rcvr_expr.span,
             "must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method",
         );
-        if let ExprKind::Lit(_) = args.receiver.kind {
+        if let ExprKind::Lit(_) = rcvr_expr.kind {
             err.span_help(
-                args.receiver.span.shrink_to_lo(),
+                rcvr_expr.span.shrink_to_lo(),
                 "a writer is needed before this format string",
             );
         };
@@ -296,7 +288,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         rcvr_ty: Ty<'tcx>,
         item_name: Ident,
         source: SelfSource<'tcx>,
-        args: Option<MethodCallComponents<'tcx>>,
+        args: Option<&'tcx [hir::Expr<'tcx>]>,
         sugg_span: Span,
         no_match_data: &mut NoMatchData<'tcx>,
         expected: Expectation<'tcx>,
@@ -377,23 +369,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             tcx.is_diagnostic_item(sym::write_macro, def_id)
                 || tcx.is_diagnostic_item(sym::writeln_macro, def_id)
         }) && item_name.name == Symbol::intern("write_fmt");
-        let mut err = if is_write && let Some(args) = args {
-            self.suggest_missing_writer(rcvr_ty, args)
-        } else {
-            tcx.sess.create_err(NoAssociatedItem {
-                span,
-                item_kind,
-                item_name,
-                ty_prefix: if trait_missing_method {
-                    // FIXME(mu001999) E0599 maybe not suitable here because it is for types
-                    Cow::from("trait")
-                } else {
-                    rcvr_ty.prefix_string(self.tcx)
-                },
-                ty_str: ty_str_reported,
-                trait_missing_method,
-            })
-        };
+        let mut err =
+            if is_write && let SelfSource::MethodCall(rcvr_expr) = source
+            {
+                self.suggest_missing_writer(rcvr_ty, rcvr_expr)
+            } else {
+                tcx.sess.create_err(NoAssociatedItem {
+                    span,
+                    item_kind,
+                    item_name,
+                    ty_prefix: if trait_missing_method {
+                        // FIXME(mu001999) E0599 maybe not suitable here because it is for types
+                        Cow::from("trait")
+                    } else {
+                        rcvr_ty.prefix_string(self.tcx)
+                    },
+                    ty_str: ty_str_reported,
+                    trait_missing_method,
+                })
+            };
         if tcx.sess.source_map().is_multiline(sugg_span) {
             err.span_label(sugg_span.with_hi(span.lo()), "");
         }
@@ -409,6 +403,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             err.downgrade_to_delayed_bug();
         }
 
+        if matches!(source, SelfSource::QPath(_)) && args.is_some() {
+            self.find_builder_fn(&mut err, rcvr_ty);
+        }
+
         if tcx.ty_is_opaque_future(rcvr_ty) && item_name.name == sym::poll {
             err.help(format!(
                 "method `poll` found on `Pin<&mut {ty_str}>`, \
@@ -522,6 +520,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self.note_candidates_on_method_error(
                 rcvr_ty,
                 item_name,
+                source,
                 args,
                 span,
                 &mut err,
@@ -532,6 +531,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self.note_candidates_on_method_error(
                 rcvr_ty,
                 item_name,
+                source,
                 args,
                 span,
                 &mut err,
@@ -975,7 +975,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 unsatisfied_bounds = true;
             }
         } else if let ty::Adt(def, targs) = rcvr_ty.kind()
-            && let Some(args) = args
+            && let SelfSource::MethodCall(rcvr_expr) = source
         {
             // This is useful for methods on arbitrary self types that might have a simple
             // mutability difference, like calling a method on `Pin<&mut Self>` that is on
@@ -998,8 +998,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         rcvr_ty,
                         &item_segment,
                         span,
-                        args.full_expr,
-                        args.receiver,
+                        tcx.hir().get_parent(rcvr_expr.hir_id).expect_expr(),
+                        rcvr_expr,
                     ) {
                         err.span_note(
                             tcx.def_span(method.def_id),
@@ -1168,7 +1168,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 span,
                 rcvr_ty,
                 item_name,
-                args.map(|MethodCallComponents { args, .. }| args.len() + 1),
+                args.map(|args| args.len() + 1),
                 source,
                 no_match_data.out_of_scope_traits.clone(),
                 &unsatisfied_predicates,
@@ -1249,7 +1249,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         rcvr_ty: Ty<'tcx>,
         item_name: Ident,
-        args: Option<MethodCallComponents<'tcx>>,
+        self_source: SelfSource<'tcx>,
+        args: Option<&'tcx [hir::Expr<'tcx>]>,
         span: Span,
         err: &mut Diagnostic,
         sources: &mut Vec<CandidateSource>,
@@ -1319,38 +1320,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                     if let Some(sugg_span) = sugg_span
                         && let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did)
-                    {
-                        let path = self.tcx.def_path_str(trait_ref.skip_binder().def_id);
-
-                        let ty = match item.kind {
-                            ty::AssocKind::Const | ty::AssocKind::Type => impl_ty,
-                            ty::AssocKind::Fn => self
-                                .tcx
-                                .fn_sig(item.def_id)
-                                .instantiate_identity()
-                                .inputs()
-                                .skip_binder()
-                                .get(0)
-                                .filter(|ty| ty.is_ref() && !rcvr_ty.is_ref())
-                                .copied()
-                                .unwrap_or(rcvr_ty),
-                        };
-                        if let Some(sugg) = print_disambiguation_help(
-                            item_name,
-                            args,
+                        && let Some(sugg) = print_disambiguation_help(
+                            self.tcx,
                             err,
-                            path,
-                            ty,
-                            Some(impl_ty),
-                            item.kind,
-                            self.tcx.def_kind_descr(item.kind.as_def_kind(), item.def_id),
-                            sugg_span,
+                            self_source,
+                            args,
+                            trait_ref.instantiate(
+                                self.tcx,
+                                self.fresh_args_for_item(sugg_span, impl_did)
+                            ).with_self_ty(self.tcx, rcvr_ty),
                             idx,
-                            self.tcx.sess.source_map(),
-                            item.fn_has_self_parameter,
-                        ) {
-                            suggs.push(sugg);
-                        }
+                            sugg_span,
+                            item,
+                        )
+                    {
+                        suggs.push(sugg);
                     }
                 }
                 CandidateSource::Trait(trait_did) => {
@@ -1372,24 +1356,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         err.span_note(item_span, msg);
                         None
                     };
-                    if let Some(sugg_span) = sugg_span {
-                        let path = self.tcx.def_path_str(trait_did);
-                        if let Some(sugg) = print_disambiguation_help(
-                            item_name,
-                            args,
+                    if let Some(sugg_span) = sugg_span
+                        && let Some(sugg) = print_disambiguation_help(
+                            self.tcx,
                             err,
-                            path,
-                            rcvr_ty,
-                            None,
-                            item.kind,
-                            self.tcx.def_kind_descr(item.kind.as_def_kind(), item.def_id),
-                            sugg_span,
+                            self_source,
+                            args,
+                            ty::TraitRef::new(
+                                self.tcx,
+                                trait_did,
+                                self.fresh_args_for_item(sugg_span, trait_did)
+                            ).with_self_ty(self.tcx, rcvr_ty),
                             idx,
-                            self.tcx.sess.source_map(),
-                            item.fn_has_self_parameter,
-                        ) {
-                            suggs.push(sugg);
-                        }
+                            sugg_span,
+                            item,
+                        )
+                    {
+                        suggs.push(sugg);
                     }
                 }
             }
@@ -1407,6 +1390,85 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    /// Look at all the associated functions without receivers in the type's inherent impls
+    /// to look for builders that return `Self`, `Option<Self>` or `Result<Self, _>`.
+    fn find_builder_fn(&self, err: &mut Diagnostic, rcvr_ty: Ty<'tcx>) {
+        let ty::Adt(adt_def, _) = rcvr_ty.kind() else {
+            return;
+        };
+        let mut items = self
+            .tcx
+            .inherent_impls(adt_def.did())
+            .iter()
+            .flat_map(|i| self.tcx.associated_items(i).in_definition_order())
+            // Only assoc fn with no receivers.
+            .filter(|item| matches!(item.kind, ty::AssocKind::Fn) && !item.fn_has_self_parameter)
+            .filter_map(|item| {
+                // Only assoc fns that return `Self`, `Option<Self>` or `Result<Self, _>`.
+                let ret_ty = self.tcx.fn_sig(item.def_id).skip_binder().output();
+                let ret_ty = self.tcx.erase_late_bound_regions(ret_ty);
+                let ty::Adt(def, args) = ret_ty.kind() else {
+                    return None;
+                };
+                // Check for `-> Self`
+                if self.can_eq(self.param_env, ret_ty, rcvr_ty) {
+                    return Some((item.def_id, ret_ty));
+                }
+                // Check for `-> Option<Self>` or `-> Result<Self, _>`
+                if ![self.tcx.lang_items().option_type(), self.tcx.get_diagnostic_item(sym::Result)]
+                    .contains(&Some(def.did()))
+                {
+                    return None;
+                }
+                let arg = args.get(0)?.expect_ty();
+                if self.can_eq(self.param_env, rcvr_ty, arg) {
+                    Some((item.def_id, ret_ty))
+                } else {
+                    None
+                }
+            })
+            .collect::<Vec<_>>();
+        let post = if items.len() > 5 {
+            let items_len = items.len();
+            items.truncate(4);
+            format!("\nand {} others", items_len - 4)
+        } else {
+            String::new()
+        };
+        match &items[..] {
+            [] => {}
+            [(def_id, ret_ty)] => {
+                err.span_note(
+                    self.tcx.def_span(def_id),
+                    format!(
+                        "if you're trying to build a new `{rcvr_ty}`, consider using `{}` which \
+                         returns `{ret_ty}`",
+                        self.tcx.def_path_str(def_id),
+                    ),
+                );
+            }
+            _ => {
+                let span: MultiSpan = items
+                    .iter()
+                    .map(|(def_id, _)| self.tcx.def_span(def_id))
+                    .collect::<Vec<Span>>()
+                    .into();
+                err.span_note(
+                    span,
+                    format!(
+                        "if you're trying to build a new `{rcvr_ty}` consider using one of the \
+                         following associated functions:\n{}{post}",
+                        items
+                            .iter()
+                            .map(|(def_id, _ret_ty)| self.tcx.def_path_str(def_id))
+                            .collect::<Vec<String>>()
+                            .join("\n")
+                    ),
+                );
+            }
+        }
+    }
+
     /// Suggest calling `Ty::method` if `.method()` isn't found because the method
     /// doesn't take a `self` receiver.
     fn suggest_associated_call_syntax(
@@ -1416,7 +1478,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         rcvr_ty: Ty<'tcx>,
         source: SelfSource<'tcx>,
         item_name: Ident,
-        args: Option<MethodCallComponents<'tcx>>,
+        args: Option<&'tcx [hir::Expr<'tcx>]>,
         sugg_span: Span,
     ) {
         let mut has_unsuggestable_args = false;
@@ -1490,38 +1552,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 None
             };
             let mut applicability = Applicability::MachineApplicable;
-            let args = if let Some(MethodCallComponents { receiver, args, .. }) = args {
-                // The first arg is the same kind as the receiver
-                let explicit_args = if first_arg.is_some() {
-                    std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>()
+            let args = if let SelfSource::MethodCall(receiver) = source
+                    && let Some(args) = args
+                {
+                    // The first arg is the same kind as the receiver
+                    let explicit_args = if first_arg.is_some() {
+                        std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>()
+                    } else {
+                        // There is no `Self` kind to infer the arguments from
+                        if has_unsuggestable_args {
+                            applicability = Applicability::HasPlaceholders;
+                        }
+                        args.iter().collect()
+                    };
+                    format!(
+                        "({}{})",
+                        first_arg.unwrap_or(""),
+                        explicit_args
+                            .iter()
+                            .map(|arg| self
+                                .tcx
+                                .sess
+                                .source_map()
+                                .span_to_snippet(arg.span)
+                                .unwrap_or_else(|_| {
+                                    applicability = Applicability::HasPlaceholders;
+                                    "_".to_owned()
+                                }))
+                            .collect::<Vec<_>>()
+                            .join(", "),
+                    )
                 } else {
-                    // There is no `Self` kind to infer the arguments from
-                    if has_unsuggestable_args {
-                        applicability = Applicability::HasPlaceholders;
-                    }
-                    args.iter().collect()
+                    applicability = Applicability::HasPlaceholders;
+                    "(...)".to_owned()
                 };
-                format!(
-                    "({}{})",
-                    first_arg.unwrap_or(""),
-                    explicit_args
-                        .iter()
-                        .map(|arg| self
-                            .tcx
-                            .sess
-                            .source_map()
-                            .span_to_snippet(arg.span)
-                            .unwrap_or_else(|_| {
-                                applicability = Applicability::HasPlaceholders;
-                                "_".to_owned()
-                            }))
-                        .collect::<Vec<_>>()
-                        .join(", "),
-                )
-            } else {
-                applicability = Applicability::HasPlaceholders;
-                "(...)".to_owned()
-            };
             err.span_suggestion(
                 sugg_span,
                 "use associated function syntax instead",
@@ -3179,56 +3243,59 @@ pub fn all_traits(tcx: TyCtxt<'_>) -> Vec<TraitInfo> {
 }
 
 fn print_disambiguation_help<'tcx>(
-    item_name: Ident,
-    args: Option<MethodCallComponents<'tcx>>,
+    tcx: TyCtxt<'tcx>,
     err: &mut Diagnostic,
-    trait_name: String,
-    rcvr_ty: Ty<'_>,
-    impl_self_ty: Option<Ty<'_>>,
-    kind: ty::AssocKind,
-    def_kind_descr: &'static str,
+    source: SelfSource<'tcx>,
+    args: Option<&'tcx [hir::Expr<'tcx>]>,
+    trait_ref: ty::TraitRef<'tcx>,
+    candidate_idx: Option<usize>,
     span: Span,
-    candidate: Option<usize>,
-    source_map: &source_map::SourceMap,
-    fn_has_self_parameter: bool,
+    item: ty::AssocItem,
 ) -> Option<String> {
+    let trait_ref = if item.fn_has_self_parameter {
+        trait_ref.print_only_trait_name().to_string()
+    } else {
+        format!("<{} as {}>", trait_ref.args[0], trait_ref.print_only_trait_name())
+    };
     Some(
-        if let (ty::AssocKind::Fn, Some(MethodCallComponents { receiver, args, .. })) = (kind, args)
+        if matches!(item.kind, ty::AssocKind::Fn)
+            && let SelfSource::MethodCall(receiver) = source
+            && let Some(args) = args
         {
+            let def_kind_descr = tcx.def_kind_descr(item.kind.as_def_kind(), item.def_id);
+            let item_name = item.ident(tcx);
+            let rcvr_ref = tcx.fn_sig(item.def_id).skip_binder().skip_binder().inputs()[0]
+                .ref_mutability()
+                .map_or("", |mutbl| mutbl.ref_prefix_str());
             let args = format!(
                 "({}{})",
-                rcvr_ty.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str()),
+                rcvr_ref,
                 std::iter::once(receiver)
                     .chain(args.iter())
-                    .map(|arg| source_map
+                    .map(|arg| tcx
+                        .sess
+                        .source_map()
                         .span_to_snippet(arg.span)
                         .unwrap_or_else(|_| { "_".to_owned() }))
                     .collect::<Vec<_>>()
                     .join(", "),
             );
-            let trait_name = if !fn_has_self_parameter && let Some(impl_self_ty) = impl_self_ty {
-            format!("<{impl_self_ty} as {trait_name}>")
-        } else {
-            trait_name
-        };
             err.span_suggestion_verbose(
                 span,
                 format!(
                     "disambiguate the {def_kind_descr} for {}",
-                    if let Some(candidate) = candidate {
+                    if let Some(candidate) = candidate_idx {
                         format!("candidate #{candidate}")
                     } else {
                         "the candidate".to_string()
                     },
                 ),
-                format!("{trait_name}::{item_name}{args}"),
+                format!("{trait_ref}::{item_name}{args}"),
                 Applicability::HasPlaceholders,
             );
             return None;
-        } else if let Some(impl_self_ty) = impl_self_ty {
-            format!("<{impl_self_ty} as {trait_name}>::")
         } else {
-            format!("{trait_name}::")
+            format!("{trait_ref}::")
         },
     )
 }
diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs
index d0d3b0e5b73..f40406c6726 100644
--- a/compiler/rustc_hir_typeck/src/op.rs
+++ b/compiler/rustc_hir_typeck/src/op.rs
@@ -379,6 +379,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         (err, output_def_id)
                     }
                 };
+                if self.check_for_missing_semi(expr, &mut err)
+                    && let hir::Node::Expr(expr) = self.tcx.hir().get_parent(expr.hir_id)
+                    && let hir::ExprKind::Assign(..) = expr.kind
+                {
+                    // We defer to the later error produced by `check_lhs_assignable`.
+                    err.delay_as_bug();
+                }
 
                 let suggest_deref_binop =
                     |err: &mut DiagnosticBuilder<'_, _>, lhs_deref_ty: Ty<'tcx>| {
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 50684482c0d..b30f9b82fbb 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -166,9 +166,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) {
         let PatInfo { binding_mode: def_bm, top_info: ti, .. } = pat_info;
         let path_res = match &pat.kind {
-            PatKind::Path(qpath) => {
-                Some(self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span))
-            }
+            PatKind::Path(qpath) => Some(
+                self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span, None),
+            ),
             _ => None,
         };
         let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res));
@@ -1060,7 +1060,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Resolve the path and check the definition for errors.
         let (res, opt_ty, segments) =
-            self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span);
+            self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span, None);
         if res == Res::Err {
             let e = tcx.sess.delay_span_bug(pat.span, "`Res::Err` but no error emitted");
             self.set_tainted_by_errors(e);
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index 75c70ec59fb..17b81acd506 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -424,7 +424,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 origin = updated.1;
 
                 let (place, capture_kind) = match capture_clause {
-                    hir::CaptureBy::Value => adjust_for_move_closure(place, capture_kind),
+                    hir::CaptureBy::Value { .. } => adjust_for_move_closure(place, capture_kind),
                     hir::CaptureBy::Ref => adjust_for_non_move_closure(place, capture_kind),
                 };
 
@@ -958,7 +958,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let ty = self.resolve_vars_if_possible(self.node_ty(var_hir_id));
 
         let ty = match closure_clause {
-            hir::CaptureBy::Value => ty, // For move closure the capture kind should be by value
+            hir::CaptureBy::Value { .. } => ty, // For move closure the capture kind should be by value
             hir::CaptureBy::Ref => {
                 // For non move closure the capture kind is the max capture kind of all captures
                 // according to the ordering ImmBorrow < UniqueImmBorrow < MutBorrow < ByValue
@@ -1073,7 +1073,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             match closure_clause {
                 // Only migrate if closure is a move closure
-                hir::CaptureBy::Value => {
+                hir::CaptureBy::Value { .. } => {
                     let mut diagnostics_info = FxIndexSet::default();
                     let upvars =
                         self.tcx.upvars_mentioned(closure_def_id).expect("must be an upvar");
@@ -1479,10 +1479,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // If the data will be moved out of this place, then the place will be truncated
             // at the first Deref in `adjust_upvar_borrow_kind_for_consume` and then moved into
             // the closure.
-            hir::CaptureBy::Value if !place.deref_tys().any(Ty::is_ref) => {
+            hir::CaptureBy::Value { .. } if !place.deref_tys().any(Ty::is_ref) => {
                 ty::UpvarCapture::ByValue
             }
-            hir::CaptureBy::Value | hir::CaptureBy::Ref => ty::UpvarCapture::ByRef(ty::ImmBorrow),
+            hir::CaptureBy::Value { .. } | hir::CaptureBy::Ref => {
+                ty::UpvarCapture::ByRef(ty::ImmBorrow)
+            }
         }
     }
 
diff --git a/compiler/rustc_incremental/messages.ftl b/compiler/rustc_incremental/messages.ftl
index 5d885e07192..e74173b24a9 100644
--- a/compiler/rustc_incremental/messages.ftl
+++ b/compiler/rustc_incremental/messages.ftl
@@ -30,8 +30,6 @@ incremental_create_lock =
     incremental compilation: could not create session directory lock file: {$lock_err}
 incremental_create_new = failed to create {$name} at `{$path}`: {$err}
 
-incremental_decode_incr_cache = could not decode incremental cache: {$err}
-
 incremental_delete_full = error deleting incremental compilation session directory `{$path}`: {$err}
 
 incremental_delete_incompatible =
diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/errors.rs
index 05ed4f7598d..61bb0353a9f 100644
--- a/compiler/rustc_incremental/src/errors.rs
+++ b/compiler/rustc_incremental/src/errors.rs
@@ -271,12 +271,6 @@ pub struct LoadDepGraph {
 }
 
 #[derive(Diagnostic)]
-#[diag(incremental_decode_incr_cache)]
-pub struct DecodeIncrCache {
-    pub err: String,
-}
-
-#[derive(Diagnostic)]
 #[diag(incremental_write_dep_graph)]
 pub struct WriteDepGraph<'a> {
     pub path: &'a Path,
diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs
index cbd55fe4205..6dfc4096910 100644
--- a/compiler/rustc_incremental/src/persist/load.rs
+++ b/compiler/rustc_incremental/src/persist/load.rs
@@ -30,8 +30,6 @@ pub enum LoadResult<T> {
     DataOutOfDate,
     /// Loading the dep graph failed.
     LoadDepGraph(PathBuf, std::io::Error),
-    /// Decoding loaded incremental cache failed.
-    DecodeIncrCache(Box<dyn std::any::Any + Send>),
 }
 
 impl<T: Default> LoadResult<T> {
@@ -44,9 +42,7 @@ impl<T: Default> LoadResult<T> {
             }
             (
                 Some(IncrementalStateAssertion::Loaded),
-                LoadResult::LoadDepGraph(..)
-                | LoadResult::DecodeIncrCache(..)
-                | LoadResult::DataOutOfDate,
+                LoadResult::LoadDepGraph(..) | LoadResult::DataOutOfDate,
             ) => {
                 sess.emit_fatal(errors::AssertLoaded);
             }
@@ -58,10 +54,6 @@ impl<T: Default> LoadResult<T> {
                 sess.emit_warning(errors::LoadDepGraph { path, err });
                 Default::default()
             }
-            LoadResult::DecodeIncrCache(err) => {
-                sess.emit_warning(errors::DecodeIncrCache { err: format!("{err:?}") });
-                Default::default()
-            }
             LoadResult::DataOutOfDate => {
                 if let Err(err) = delete_all_session_dir_contents(sess) {
                     sess.emit_err(errors::DeleteIncompatible { path: dep_graph_path(sess), err });
@@ -150,7 +142,6 @@ fn load_dep_graph(sess: &Session) -> LoadResult<(SerializedDepGraph, WorkProduct
     match load_data(&path, sess) {
         LoadResult::DataOutOfDate => LoadResult::DataOutOfDate,
         LoadResult::LoadDepGraph(path, err) => LoadResult::LoadDepGraph(path, err),
-        LoadResult::DecodeIncrCache(err) => LoadResult::DecodeIncrCache(err),
         LoadResult::Ok { data: (bytes, start_pos) } => {
             let mut decoder = MemDecoder::new(&bytes, start_pos);
             let prev_commandline_args_hash = u64::decode(&mut decoder);
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index e4be435fded..26d071a0139 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -2444,18 +2444,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 let suggestion =
                     if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") };
                 suggs.push((sp, suggestion))
-            } else {
-                let generics = self.tcx.hir().get_generics(suggestion_scope).unwrap();
+            } else if let Some(generics) = self.tcx.hir().get_generics(suggestion_scope) {
                 let pred = format!("{bound_kind}: {lt_name}");
-                let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred,);
+                let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred);
                 suggs.push((generics.tail_span_for_predicate_suggestion(), suggestion))
+            } else {
+                let consider = format!("{msg} `{bound_kind}: {sub}`...");
+                err.help(consider);
             }
 
-            err.multipart_suggestion_verbose(
-                format!("{msg}"),
-                suggs,
-                Applicability::MaybeIncorrect, // Issue #41966
-            );
+            if !suggs.is_empty() {
+                err.multipart_suggestion_verbose(
+                    format!("{msg}"),
+                    suggs,
+                    Applicability::MaybeIncorrect, // Issue #41966
+                );
+            }
         }
 
         err
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 8e66083a390..d3081695523 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -4,11 +4,11 @@ use rustc_data_structures::profiling::TimePassesFormat;
 use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig};
 use rustc_session::config::{
     build_configuration, build_session_options, rustc_optgroups, BranchProtection, CFGuard, Cfg,
-    DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry, ExternLocation, Externs, Input,
-    InstrumentCoverage, InstrumentXRay, LinkSelfContained, LinkerPluginLto, LocationDetail, LtoCli,
-    MirSpanview, OomStrategy, Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet,
-    Passes, Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion,
-    TraitSolver, WasiExecModel,
+    DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry, ExternLocation, Externs,
+    InliningThreshold, Input, InstrumentCoverage, InstrumentXRay, LinkSelfContained,
+    LinkerPluginLto, LocationDetail, LtoCli, MirSpanview, OomStrategy, Options, OutFileName,
+    OutputType, OutputTypes, PAuthKey, PacRet, Passes, Polonius, ProcMacroExecutionStrategy, Strip,
+    SwitchWithOptPath, SymbolManglingVersion, TraitSolver, WasiExecModel,
 };
 use rustc_session::lint::Level;
 use rustc_session::search_paths::SearchPath;
@@ -748,7 +748,7 @@ fn test_unstable_options_tracking_hash() {
     );
     tracked!(codegen_backend, Some("abc".to_string()));
     tracked!(crate_attr, vec!["abc".to_string()]);
-    tracked!(cross_crate_inline_threshold, Some(200));
+    tracked!(cross_crate_inline_threshold, InliningThreshold::Always);
     tracked!(debug_info_for_profiling, true);
     tracked!(debug_macros, true);
     tracked!(dep_info_omit_d_target, true);
diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
index 3f2bf2c9b44..142384e6d0c 100644
--- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
+++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
@@ -25,7 +25,6 @@
 #include "llvm/Transforms/IPO.h"
 #include "llvm/Transforms/Instrumentation.h"
 #include "llvm/Transforms/Scalar.h"
-#include "llvm/Transforms/Vectorize.h"
 
 #define LLVM_VERSION_GE(major, minor)                                          \
   (LLVM_VERSION_MAJOR > (major) ||                                             \
diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs
index ba75517d7a6..047066ac681 100644
--- a/compiler/rustc_macros/src/serialize.rs
+++ b/compiler/rustc_macros/src/serialize.rs
@@ -5,11 +5,16 @@ use syn::spanned::Spanned;
 
 pub fn type_decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
     let decoder_ty = quote! { __D };
-    if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") {
-        s.add_impl_generic(parse_quote! { 'tcx });
-    }
-    s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_type_ir::codec::TyDecoder<I = ::rustc_middle::ty::TyCtxt<'tcx>>});
-    s.add_bounds(synstructure::AddBounds::Generics);
+    let bound = if s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") {
+        quote! { <I = ::rustc_middle::ty::TyCtxt<'tcx>> }
+    } else if s.ast().generics.type_params().any(|ty| ty.ident == "I") {
+        quote! { <I = I> }
+    } else {
+        quote! {}
+    };
+
+    s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_type_ir::codec::TyDecoder #bound });
+    s.add_bounds(synstructure::AddBounds::Fields);
 
     decodable_body(s, decoder_ty)
 }
@@ -97,12 +102,17 @@ fn decode_field(field: &syn::Field) -> proc_macro2::TokenStream {
 }
 
 pub fn type_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
-    if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") {
-        s.add_impl_generic(parse_quote! {'tcx});
-    }
+    let bound = if s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") {
+        quote! { <I = ::rustc_middle::ty::TyCtxt<'tcx>> }
+    } else if s.ast().generics.type_params().any(|ty| ty.ident == "I") {
+        quote! { <I = I> }
+    } else {
+        quote! {}
+    };
+
     let encoder_ty = quote! { __E };
-    s.add_impl_generic(parse_quote! {#encoder_ty: ::rustc_type_ir::codec::TyEncoder<I = ::rustc_middle::ty::TyCtxt<'tcx>>});
-    s.add_bounds(synstructure::AddBounds::Generics);
+    s.add_impl_generic(parse_quote! {#encoder_ty: ::rustc_type_ir::codec::TyEncoder #bound });
+    s.add_bounds(synstructure::AddBounds::Fields);
 
     encodable_body(s, encoder_ty, false)
 }
diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs
index ec2517b581e..b06b4fb87cd 100644
--- a/compiler/rustc_metadata/src/lib.rs
+++ b/compiler/rustc_metadata/src/lib.rs
@@ -8,6 +8,7 @@
 #![cfg_attr(not(bootstrap), feature(coroutines))]
 #![feature(iter_from_coroutine)]
 #![feature(let_chains)]
+#![feature(if_let_guard)]
 #![feature(proc_macro_internals)]
 #![feature(macro_metavar_expr)]
 #![feature(min_specialization)]
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index de436c16ca9..2042863d189 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -2386,31 +2386,32 @@ pub fn rendered_const<'tcx>(tcx: TyCtxt<'tcx>, body: hir::BodyId) -> String {
         }
     }
 
-    let classification = classify(value);
-
-    if classification == Literal
-        && !value.span.from_expansion()
-        && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(value.span)
-    {
-        // For literals, we avoid invoking the pretty-printer and use the source snippet instead to
-        // preserve certain stylistic choices the user likely made for the sake legibility like
+    match classify(value) {
+        // For non-macro literals, we avoid invoking the pretty-printer and use the source snippet
+        // instead to preserve certain stylistic choices the user likely made for the sake of
+        // legibility, like:
         //
         // * hexadecimal notation
         // * underscores
         // * character escapes
         //
         // FIXME: This passes through `-/*spacer*/0` verbatim.
-        snippet
-    } else if classification == Simple {
+        Literal if !value.span.from_expansion()
+            && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(value.span) => {
+            snippet
+        }
+
         // Otherwise we prefer pretty-printing to get rid of extraneous whitespace, comments and
         // other formatting artifacts.
-        id_to_string(&hir, body.hir_id)
-    } else if tcx.def_kind(hir.body_owner_def_id(body).to_def_id()) == DefKind::AnonConst {
+        Literal | Simple => id_to_string(&hir, body.hir_id),
+
         // FIXME: Omit the curly braces if the enclosing expression is an array literal
         //        with a repeated element (an `ExprKind::Repeat`) as in such case it
         //        would not actually need any disambiguation.
-        "{ _ }".to_owned()
-    } else {
-        "_".to_owned()
+        Complex => if tcx.def_kind(hir.body_owner_def_id(body).to_def_id()) == DefKind::AnonConst {
+            "{ _ }".to_owned()
+        } else {
+            "_".to_owned()
+        }
     }
 }
diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs
index 0b5426c3bb1..64b63f4c5eb 100644
--- a/compiler/rustc_middle/src/infer/canonical.rs
+++ b/compiler/rustc_middle/src/infer/canonical.rs
@@ -222,7 +222,7 @@ pub enum CanonicalVarKind<'tcx> {
     Effect,
 
     /// A "placeholder" that represents "any const".
-    PlaceholderConst(ty::PlaceholderConst<'tcx>, Ty<'tcx>),
+    PlaceholderConst(ty::PlaceholderConst, Ty<'tcx>),
 }
 
 impl<'tcx> CanonicalVarKind<'tcx> {
diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index e20e9d9312c..cdde6a596a8 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -2,7 +2,8 @@ use crate::mir;
 use crate::query::CyclePlaceholder;
 use crate::traits;
 use crate::ty::{self, Ty};
-use std::mem::{size_of, transmute_copy, MaybeUninit};
+use std::intrinsics::transmute_unchecked;
+use std::mem::{size_of, MaybeUninit};
 
 #[derive(Copy, Clone)]
 pub struct Erased<T: Copy> {
@@ -29,8 +30,15 @@ pub fn erase<T: EraseType>(src: T) -> Erase<T> {
     };
 
     Erased::<<T as EraseType>::Result> {
+        // `transmute_unchecked` is needed here because it does not have `transmute`'s size check
+        // (and thus allows to transmute between `T` and `MaybeUninit<T::Result>`) (we do the size
+        // check ourselves in the `const` block above).
+        //
+        // `transmute_copy` is also commonly used for this (and it would work here since
+        // `EraseType: Copy`), but `transmute_unchecked` better explains the intent.
+        //
         // SAFETY: It is safe to transmute to MaybeUninit for types with the same sizes.
-        data: unsafe { transmute_copy(&src) },
+        data: unsafe { transmute_unchecked::<T, MaybeUninit<T::Result>>(src) },
     }
 }
 
@@ -38,22 +46,24 @@ pub fn erase<T: EraseType>(src: T) -> Erase<T> {
 #[inline(always)]
 pub fn restore<T: EraseType>(value: Erase<T>) -> T {
     let value: Erased<<T as EraseType>::Result> = value;
+    // See comment in `erase` for why we use `transmute_unchecked`.
+    //
     // SAFETY: Due to the use of impl Trait in `Erase` the only way to safely create an instance
     // of `Erase` is to call `erase`, so we know that `value.data` is a valid instance of `T` of
     // the right size.
-    unsafe { transmute_copy(&value.data) }
+    unsafe { transmute_unchecked::<MaybeUninit<T::Result>, T>(value.data) }
 }
 
 impl<T> EraseType for &'_ T {
-    type Result = [u8; size_of::<*const ()>()];
+    type Result = [u8; size_of::<&'static ()>()];
 }
 
 impl<T> EraseType for &'_ [T] {
-    type Result = [u8; size_of::<*const [()]>()];
+    type Result = [u8; size_of::<&'static [()]>()];
 }
 
 impl<T> EraseType for &'_ ty::List<T> {
-    type Result = [u8; size_of::<*const ()>()];
+    type Result = [u8; size_of::<&'static ty::List<()>>()];
 }
 
 impl<I: rustc_index::Idx, T> EraseType for &'_ rustc_index::IndexSlice<I, T> {
diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs
index d03d92c3a4b..8feefb4c03c 100644
--- a/compiler/rustc_middle/src/thir/visit.rs
+++ b/compiler/rustc_middle/src/thir/visit.rs
@@ -66,8 +66,9 @@ pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Exp
         Use { source } => visitor.visit_expr(&visitor.thir()[source]),
         NeverToAny { source } => visitor.visit_expr(&visitor.thir()[source]),
         PointerCoercion { source, cast: _ } => visitor.visit_expr(&visitor.thir()[source]),
-        Let { expr, .. } => {
+        Let { expr, ref pat } => {
             visitor.visit_expr(&visitor.thir()[expr]);
+            visitor.visit_pat(pat);
         }
         Loop { body } => visitor.visit_expr(&visitor.thir()[body]),
         Match { scrutinee, ref arms, .. } => {
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 9e2ff3820af..6cd75e08727 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -305,9 +305,14 @@ pub enum ObligationCauseCode<'tcx> {
     SizedCoroutineInterior(LocalDefId),
     /// `[expr; N]` requires `type_of(expr): Copy`.
     RepeatElementCopy {
-        /// If element is a `const fn` we display a help message suggesting to move the
-        /// function call to a new `const` item while saying that `T` doesn't implement `Copy`.
-        is_const_fn: bool,
+        /// If element is a `const fn` or const ctor we display a help message suggesting
+        /// to move it to a new `const` item while saying that `T` doesn't implement `Copy`.
+        is_constable: IsConstable,
+        elt_type: Ty<'tcx>,
+        elt_span: Span,
+        /// Span of the statement/item in which the repeat expression occurs. We can use this to
+        /// place a `const` declaration before it
+        elt_stmt_span: Span,
     },
 
     /// Types of fields (other than the last, except for packed structs) in a struct must be sized.
@@ -455,6 +460,21 @@ pub enum ObligationCauseCode<'tcx> {
     TypeAlias(InternedObligationCauseCode<'tcx>, Span, DefId),
 }
 
+/// Whether a value can be extracted into a const.
+/// Used for diagnostics around array repeat expressions.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
+pub enum IsConstable {
+    No,
+    /// Call to a const fn
+    Fn,
+    /// Use of a const ctor
+    Ctor,
+}
+
+crate::TrivialTypeTraversalAndLiftImpls! {
+    IsConstable,
+}
+
 /// The 'location' at which we try to perform HIR-based wf checking.
 /// This information is used to obtain an `hir::Ty`, which
 /// we can walk in order to obtain precise spans for any
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index cfacccd2679..af5ffc20d48 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -84,7 +84,7 @@ impl<'tcx> Const<'tcx> {
     #[inline]
     pub fn new_placeholder(
         tcx: TyCtxt<'tcx>,
-        placeholder: ty::PlaceholderConst<'tcx>,
+        placeholder: ty::PlaceholderConst,
         ty: Ty<'tcx>,
     ) -> Const<'tcx> {
         Const::new(tcx, ty::ConstKind::Placeholder(placeholder), ty)
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 68812bba42f..551c4a15dd0 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -106,7 +106,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     type Const = ty::Const<'tcx>;
     type InferConst = ty::InferConst;
     type AliasConst = ty::UnevaluatedConst<'tcx>;
-    type PlaceholderConst = ty::PlaceholderConst<'tcx>;
+    type PlaceholderConst = ty::PlaceholderConst;
     type ParamConst = ty::ParamConst;
     type BoundConst = ty::BoundVar;
     type ValueConst = ty::ValTree<'tcx>;
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index 77a50fa9276..0094825fc70 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -274,7 +274,7 @@ pub fn suggest_constraining_type_params<'a>(
                 span,
                 if span_to_replace.is_some() {
                     constraint.clone()
-                } else if constraint.starts_with("<") {
+                } else if constraint.starts_with('<') {
                     constraint.to_string()
                 } else if bound_list_non_empty {
                     format!(" + {constraint}")
diff --git a/compiler/rustc_middle/src/ty/erase_regions.rs b/compiler/rustc_middle/src/ty/erase_regions.rs
index 7895993ccff..3371ea3bec8 100644
--- a/compiler/rustc_middle/src/ty/erase_regions.rs
+++ b/compiler/rustc_middle/src/ty/erase_regions.rs
@@ -20,8 +20,8 @@ impl<'tcx> TyCtxt<'tcx> {
     where
         T: TypeFoldable<TyCtxt<'tcx>>,
     {
-        // If there's nothing to erase avoid performing the query at all
-        if !value.has_type_flags(TypeFlags::HAS_LATE_BOUND | TypeFlags::HAS_FREE_REGIONS) {
+        // If there's nothing to erase or anonymize, avoid performing the query at all
+        if !value.has_type_flags(TypeFlags::HAS_BINDER_VARS | TypeFlags::HAS_FREE_REGIONS) {
             return value;
         }
         debug!("erase_regions({:?})", value);
diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs
index a348e9f608a..ec36bdc5a51 100644
--- a/compiler/rustc_middle/src/ty/flags.rs
+++ b/compiler/rustc_middle/src/ty/flags.rs
@@ -34,26 +34,6 @@ impl FlagComputation {
         result.flags
     }
 
-    pub fn bound_var_flags(vars: &ty::List<ty::BoundVariableKind>) -> FlagComputation {
-        let mut computation = FlagComputation::new();
-
-        for bv in vars {
-            match bv {
-                ty::BoundVariableKind::Ty(_) => {
-                    computation.flags |= TypeFlags::HAS_TY_LATE_BOUND;
-                }
-                ty::BoundVariableKind::Region(_) => {
-                    computation.flags |= TypeFlags::HAS_RE_LATE_BOUND;
-                }
-                ty::BoundVariableKind::Const => {
-                    computation.flags |= TypeFlags::HAS_CT_LATE_BOUND;
-                }
-            }
-        }
-
-        computation
-    }
-
     fn add_flags(&mut self, flags: TypeFlags) {
         self.flags = self.flags | flags;
     }
@@ -77,7 +57,11 @@ impl FlagComputation {
     where
         F: FnOnce(&mut Self, T),
     {
-        let mut computation = FlagComputation::bound_var_flags(value.bound_vars());
+        let mut computation = FlagComputation::new();
+
+        if !value.bound_vars().is_empty() {
+            computation.add_flags(TypeFlags::HAS_BINDER_VARS);
+        }
 
         f(&mut computation, value.skip_binder());
 
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 739d4fa886e..e1c616ba078 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1527,7 +1527,7 @@ pub struct BoundConst<'tcx> {
     pub ty: Ty<'tcx>,
 }
 
-pub type PlaceholderConst<'tcx> = Placeholder<BoundVar>;
+pub type PlaceholderConst = Placeholder<BoundVar>;
 
 /// When type checking, we use the `ParamEnv` to track
 /// details about the set of where-clauses that are in scope at this
@@ -2011,7 +2011,7 @@ impl<'tcx> TyCtxt<'tcx> {
 
         // Generate a deterministically-derived seed from the item's path hash
         // to allow for cross-crate compilation to actually work
-        let mut field_shuffle_seed = self.def_path_hash(did).0.to_smaller_hash();
+        let mut field_shuffle_seed = self.def_path_hash(did).0.to_smaller_hash().as_u64();
 
         // If the user defined a custom seed for layout randomization, xor the item's
         // path hash with the user defined seed, this will allowing determinism while
diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs
index ab0999b3f19..8fc5c030277 100644
--- a/compiler/rustc_middle/src/ty/visit.rs
+++ b/compiler/rustc_middle/src/ty/visit.rs
@@ -494,15 +494,11 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasTypeFlagsVisitor {
         &mut self,
         t: &Binder<'tcx, T>,
     ) -> ControlFlow<Self::BreakTy> {
-        // If we're looking for any of the HAS_*_LATE_BOUND flags, we need to
-        // additionally consider the bound vars on the binder itself, even if
-        // the contents of a the binder (e.g. a `TraitRef`) doesn't reference
-        // the bound vars.
-        if self.flags.intersects(TypeFlags::HAS_LATE_BOUND) {
-            let bound_var_flags = FlagComputation::bound_var_flags(t.bound_vars());
-            if bound_var_flags.flags.intersects(self.flags) {
-                return ControlFlow::Break(FoundFlags);
-            }
+        // If we're looking for the HAS_BINDER_VARS flag, check if the
+        // binder has vars. This won't be present in the binder's bound
+        // value, so we need to check here too.
+        if self.flags.intersects(TypeFlags::HAS_BINDER_VARS) && !t.bound_vars().is_empty() {
+            return ControlFlow::Break(FoundFlags);
         }
 
         t.super_visit_with(self)
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index 32711c23dc4..54fc8f77f93 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -221,6 +221,11 @@ mir_build_non_exhaustive_omitted_pattern = some variants are not matched explici
     .help = ensure that all variants are matched explicitly by adding the suggested match arms
     .note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found
 
+mir_build_non_exhaustive_omitted_pattern_lint_on_arm = the lint level must be set on the whole match
+    .help = it no longer has any effect to set the lint level on an individual match arm
+    .label = remove this attribute
+    .suggestion = set the lint level on the whole match
+
 mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type `{$ty}` is non-empty
     .def_note = `{$peeled_ty}` defined here
     .type_note = the matched value is of type `{$ty}`
@@ -315,6 +320,7 @@ mir_build_unreachable_pattern = unreachable pattern
     .label = unreachable pattern
     .catchall_label = matches any value
 
+mir_build_unsafe_fn_safe_body = an unsafe function restricts its caller, but its body is safe by default
 mir_build_unsafe_not_inherited = items do not inherit unsafety from separate enclosing items
 
 mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_unsafe =
@@ -381,3 +387,5 @@ mir_build_unused_unsafe = unnecessary `unsafe` block
 mir_build_unused_unsafe_enclosing_block_label = because it's nested under this `unsafe` block
 
 mir_build_variant_defined_here = not covered
+
+mir_build_wrap_suggestion = consider wrapping the function body in an unsafe block
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index c3e1b55e463..a43aae6f449 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -856,7 +856,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             }
 
             PatKind::InlineConstant { ref subpattern, .. } => {
-                self.visit_primary_bindings(subpattern, pattern_user_ty.clone(), f)
+                self.visit_primary_bindings(subpattern, pattern_user_ty, f)
             }
 
             PatKind::Leaf { ref subpatterns } => {
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index 58d6be50b90..7c729016521 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -620,29 +620,63 @@ fn construct_const<'a, 'tcx>(
 ///
 /// This is required because we may still want to run MIR passes on an item
 /// with type errors, but normal MIR construction can't handle that in general.
-fn construct_error(tcx: TyCtxt<'_>, def: LocalDefId, err: ErrorGuaranteed) -> Body<'_> {
-    let span = tcx.def_span(def);
-    let hir_id = tcx.hir().local_def_id_to_hir_id(def);
-    let coroutine_kind = tcx.coroutine_kind(def);
-    let body_owner_kind = tcx.hir().body_owner_kind(def);
-
-    let ty = Ty::new_error(tcx, err);
-    let num_params = match body_owner_kind {
-        hir::BodyOwnerKind::Fn => tcx.fn_sig(def).skip_binder().inputs().skip_binder().len(),
-        hir::BodyOwnerKind::Closure => {
-            let ty = tcx.type_of(def).instantiate_identity();
-            match ty.kind() {
-                ty::Closure(_, args) => 1 + args.as_closure().sig().inputs().skip_binder().len(),
-                ty::Coroutine(..) => 2,
-                _ => bug!("expected closure or coroutine, found {ty:?}"),
-            }
+fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) -> Body<'_> {
+    let span = tcx.def_span(def_id);
+    let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+    let coroutine_kind = tcx.coroutine_kind(def_id);
+
+    let (inputs, output, yield_ty) = match tcx.def_kind(def_id) {
+        DefKind::Const
+        | DefKind::AssocConst
+        | DefKind::AnonConst
+        | DefKind::InlineConst
+        | DefKind::Static(_) => (vec![], tcx.type_of(def_id).instantiate_identity(), None),
+        DefKind::Ctor(..) | DefKind::Fn | DefKind::AssocFn => {
+            let sig = tcx.liberate_late_bound_regions(
+                def_id.to_def_id(),
+                tcx.fn_sig(def_id).instantiate_identity(),
+            );
+            (sig.inputs().to_vec(), sig.output(), None)
+        }
+        DefKind::Closure => {
+            let closure_ty = tcx.type_of(def_id).instantiate_identity();
+            let ty::Closure(_, args) = closure_ty.kind() else { bug!() };
+            let args = args.as_closure();
+            let sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), args.sig());
+            let self_ty = match args.kind() {
+                ty::ClosureKind::Fn => Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, closure_ty),
+                ty::ClosureKind::FnMut => Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, closure_ty),
+                ty::ClosureKind::FnOnce => closure_ty,
+            };
+            ([self_ty].into_iter().chain(sig.inputs().to_vec()).collect(), sig.output(), None)
+        }
+        DefKind::Coroutine => {
+            let coroutine_ty = tcx.type_of(def_id).instantiate_identity();
+            let ty::Coroutine(_, args, _) = coroutine_ty.kind() else { bug!() };
+            let args = args.as_coroutine();
+            let yield_ty = args.yield_ty();
+            let return_ty = args.return_ty();
+            let self_ty = Ty::new_adt(
+                tcx,
+                tcx.adt_def(tcx.lang_items().pin_type().unwrap()),
+                tcx.mk_args(&[Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, coroutine_ty).into()]),
+            );
+            let coroutine_state = Ty::new_adt(
+                tcx,
+                tcx.adt_def(tcx.lang_items().coroutine_state().unwrap()),
+                tcx.mk_args(&[yield_ty.into(), return_ty.into()]),
+            );
+            (vec![self_ty, args.resume_ty()], coroutine_state, Some(yield_ty))
         }
-        hir::BodyOwnerKind::Const { .. } => 0,
-        hir::BodyOwnerKind::Static(_) => 0,
+        dk => bug!("{:?} is not a body: {:?}", def_id, dk),
     };
+
+    let source_info = SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE };
+    let local_decls = IndexVec::from_iter(
+        [output].iter().chain(&inputs).map(|ty| LocalDecl::with_source_info(*ty, source_info)),
+    );
     let mut cfg = CFG { basic_blocks: IndexVec::new() };
     let mut source_scopes = IndexVec::new();
-    let mut local_decls = IndexVec::from_elem_n(LocalDecl::new(ty, span), 1);
 
     cfg.start_new_block();
     source_scopes.push(SourceScopeData {
@@ -655,28 +689,24 @@ fn construct_error(tcx: TyCtxt<'_>, def: LocalDefId, err: ErrorGuaranteed) -> Bo
             safety: Safety::Safe,
         }),
     });
-    let source_info = SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE };
 
-    // Some MIR passes will expect the number of parameters to match the
-    // function declaration.
-    for _ in 0..num_params {
-        local_decls.push(LocalDecl::with_source_info(ty, source_info));
-    }
     cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable);
 
     let mut body = Body::new(
-        MirSource::item(def.to_def_id()),
+        MirSource::item(def_id.to_def_id()),
         cfg.basic_blocks,
         source_scopes,
         local_decls,
         IndexVec::new(),
-        num_params,
+        inputs.len(),
         vec![],
         span,
         coroutine_kind,
-        Some(err),
+        Some(guar),
     );
-    body.coroutine.as_mut().map(|gen| gen.yield_ty = Some(ty));
+
+    body.coroutine.as_mut().map(|gen| gen.yield_ty = yield_ty);
+
     body
 }
 
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index 45be5015371..3d895c131a8 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -35,6 +35,10 @@ struct UnsafetyVisitor<'a, 'tcx> {
     param_env: ParamEnv<'tcx>,
     inside_adt: bool,
     warnings: &'a mut Vec<UnusedUnsafeWarning>,
+
+    /// Flag to ensure that we only suggest wrapping the entire function body in
+    /// an unsafe block once.
+    suggest_unsafe_block: bool,
 }
 
 impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
@@ -95,7 +99,13 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
             SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
             SafetyContext::UnsafeFn => {
                 // unsafe_op_in_unsafe_fn is disallowed
-                kind.emit_unsafe_op_in_unsafe_fn_lint(self.tcx, self.hir_context, span);
+                kind.emit_unsafe_op_in_unsafe_fn_lint(
+                    self.tcx,
+                    self.hir_context,
+                    span,
+                    self.suggest_unsafe_block,
+                );
+                self.suggest_unsafe_block = false;
             }
             SafetyContext::Safe => {
                 kind.emit_requires_unsafe_err(
@@ -297,6 +307,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
             }
             PatKind::InlineConstant { def, .. } => {
                 self.visit_inner_body(*def);
+                visit::walk_pat(self, pat);
             }
             _ => {
                 visit::walk_pat(self, pat);
@@ -394,7 +405,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                 }
             }
             ExprKind::Deref { arg } => {
-                if let ExprKind::StaticRef { def_id, .. } = self.thir[arg].kind {
+                if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) =
+                    self.thir[arg].kind
+                {
                     if self.tcx.is_mutable_static(def_id) {
                         self.requires_unsafe(expr.span, UseOfMutableStatic);
                     } else if self.tcx.is_foreign_item(def_id) {
@@ -482,14 +495,6 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                     }
                 }
             }
-            ExprKind::Let { expr: expr_id, .. } => {
-                let let_expr = &self.thir[expr_id];
-                if let ty::Adt(adt_def, _) = let_expr.ty.kind()
-                    && adt_def.is_union()
-                {
-                    self.requires_unsafe(expr.span, AccessToUnionField);
-                }
-            }
             _ => {}
         }
         visit::walk_expr(self, expr);
@@ -543,7 +548,22 @@ impl UnsafeOpKind {
         tcx: TyCtxt<'_>,
         hir_id: hir::HirId,
         span: Span,
+        suggest_unsafe_block: bool,
     ) {
+        let parent_id = tcx.hir().get_parent_item(hir_id);
+        let parent_owner = tcx.hir().owner(parent_id);
+        let should_suggest = parent_owner.fn_sig().map_or(false, |sig| sig.header.is_unsafe());
+        let unsafe_not_inherited_note = if should_suggest {
+            suggest_unsafe_block.then(|| {
+                let body_span = tcx.hir().body(parent_owner.body_id().unwrap()).value.span;
+                UnsafeNotInheritedLintNote {
+                    signature_span: tcx.def_span(parent_id.def_id),
+                    body_span,
+                }
+            })
+        } else {
+            None
+        };
         // FIXME: ideally we would want to trim the def paths, but this is not
         // feasible with the current lint emission API (see issue #106126).
         match self {
@@ -554,61 +574,89 @@ impl UnsafeOpKind {
                 UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe {
                     span,
                     function: &with_no_trimmed_paths!(tcx.def_path_str(*did)),
+                    unsafe_not_inherited_note,
                 },
             ),
             CallToUnsafeFunction(None) => tcx.emit_spanned_lint(
                 UNSAFE_OP_IN_UNSAFE_FN,
                 hir_id,
                 span,
-                UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { span },
+                UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless {
+                    span,
+                    unsafe_not_inherited_note,
+                },
             ),
             UseOfInlineAssembly => tcx.emit_spanned_lint(
                 UNSAFE_OP_IN_UNSAFE_FN,
                 hir_id,
                 span,
-                UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { span },
+                UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe {
+                    span,
+                    unsafe_not_inherited_note,
+                },
             ),
             InitializingTypeWith => tcx.emit_spanned_lint(
                 UNSAFE_OP_IN_UNSAFE_FN,
                 hir_id,
                 span,
-                UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { span },
+                UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe {
+                    span,
+                    unsafe_not_inherited_note,
+                },
             ),
             UseOfMutableStatic => tcx.emit_spanned_lint(
                 UNSAFE_OP_IN_UNSAFE_FN,
                 hir_id,
                 span,
-                UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { span },
+                UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe {
+                    span,
+                    unsafe_not_inherited_note,
+                },
             ),
             UseOfExternStatic => tcx.emit_spanned_lint(
                 UNSAFE_OP_IN_UNSAFE_FN,
                 hir_id,
                 span,
-                UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { span },
+                UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe {
+                    span,
+                    unsafe_not_inherited_note,
+                },
             ),
             DerefOfRawPointer => tcx.emit_spanned_lint(
                 UNSAFE_OP_IN_UNSAFE_FN,
                 hir_id,
                 span,
-                UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { span },
+                UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe {
+                    span,
+                    unsafe_not_inherited_note,
+                },
             ),
             AccessToUnionField => tcx.emit_spanned_lint(
                 UNSAFE_OP_IN_UNSAFE_FN,
                 hir_id,
                 span,
-                UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { span },
+                UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe {
+                    span,
+                    unsafe_not_inherited_note,
+                },
             ),
             MutationOfLayoutConstrainedField => tcx.emit_spanned_lint(
                 UNSAFE_OP_IN_UNSAFE_FN,
                 hir_id,
                 span,
-                UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe { span },
+                UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe {
+                    span,
+                    unsafe_not_inherited_note,
+                },
             ),
             BorrowOfLayoutConstrainedField => tcx.emit_spanned_lint(
                 UNSAFE_OP_IN_UNSAFE_FN,
                 hir_id,
                 span,
-                UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe { span },
+                UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe {
+                    span,
+                    unsafe_not_inherited_note,
+                },
             ),
             CallToFunctionWith(did) => tcx.emit_spanned_lint(
                 UNSAFE_OP_IN_UNSAFE_FN,
@@ -617,6 +665,7 @@ impl UnsafeOpKind {
                 UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe {
                     span,
                     function: &with_no_trimmed_paths!(tcx.def_path_str(*did)),
+                    unsafe_not_inherited_note,
                 },
             ),
         }
@@ -831,6 +880,7 @@ pub fn thir_check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
         param_env: tcx.param_env(def),
         inside_adt: false,
         warnings: &mut warnings,
+        suggest_unsafe_block: true,
     };
     visitor.visit_expr(&thir[expr]);
 
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 5bfce3ab510..418f9bb9de9 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -29,6 +29,8 @@ pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe<'a> {
     #[label]
     pub span: Span,
     pub function: &'a str,
+    #[subdiagnostic]
+    pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
 }
 
 #[derive(LintDiagnostic)]
@@ -37,6 +39,8 @@ pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe<'a> {
 pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless {
     #[label]
     pub span: Span,
+    #[subdiagnostic]
+    pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
 }
 
 #[derive(LintDiagnostic)]
@@ -45,6 +49,8 @@ pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless {
 pub struct UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe {
     #[label]
     pub span: Span,
+    #[subdiagnostic]
+    pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
 }
 
 #[derive(LintDiagnostic)]
@@ -53,6 +59,8 @@ pub struct UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe {
 pub struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe {
     #[label]
     pub span: Span,
+    #[subdiagnostic]
+    pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
 }
 
 #[derive(LintDiagnostic)]
@@ -61,6 +69,8 @@ pub struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe {
 pub struct UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe {
     #[label]
     pub span: Span,
+    #[subdiagnostic]
+    pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
 }
 
 #[derive(LintDiagnostic)]
@@ -69,6 +79,8 @@ pub struct UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe {
 pub struct UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe {
     #[label]
     pub span: Span,
+    #[subdiagnostic]
+    pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
 }
 
 #[derive(LintDiagnostic)]
@@ -77,6 +89,8 @@ pub struct UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe {
 pub struct UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe {
     #[label]
     pub span: Span,
+    #[subdiagnostic]
+    pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
 }
 
 #[derive(LintDiagnostic)]
@@ -85,6 +99,8 @@ pub struct UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe {
 pub struct UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe {
     #[label]
     pub span: Span,
+    #[subdiagnostic]
+    pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
 }
 
 #[derive(LintDiagnostic)]
@@ -93,6 +109,8 @@ pub struct UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe {
 pub struct UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe {
     #[label]
     pub span: Span,
+    #[subdiagnostic]
+    pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
 }
 
 #[derive(LintDiagnostic)]
@@ -100,6 +118,8 @@ pub struct UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe {
 pub struct UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe {
     #[label]
     pub span: Span,
+    #[subdiagnostic]
+    pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
 }
 
 #[derive(LintDiagnostic)]
@@ -109,6 +129,8 @@ pub struct UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe<'a> {
     #[label]
     pub span: Span,
     pub function: &'a str,
+    #[subdiagnostic]
+    pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
 }
 
 #[derive(Diagnostic)]
@@ -376,6 +398,27 @@ pub struct UnsafeNotInheritedNote {
     pub span: Span,
 }
 
+pub struct UnsafeNotInheritedLintNote {
+    pub signature_span: Span,
+    pub body_span: Span,
+}
+
+impl AddToDiagnostic for UnsafeNotInheritedLintNote {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+    where
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+    {
+        diag.span_note(self.signature_span, fluent::mir_build_unsafe_fn_safe_body);
+        let body_start = self.body_span.shrink_to_lo();
+        let body_end = self.body_span.shrink_to_hi();
+        diag.tool_only_multipart_suggestion(
+            fluent::mir_build_wrap_suggestion,
+            vec![(body_start, "{ unsafe ".into()), (body_end, "}".into())],
+            Applicability::MaybeIncorrect,
+        );
+    }
+}
+
 #[derive(LintDiagnostic)]
 #[diag(mir_build_unused_unsafe)]
 pub struct UnusedUnsafe {
@@ -789,6 +832,18 @@ pub(crate) struct NonExhaustiveOmittedPattern<'tcx> {
     pub uncovered: Uncovered<'tcx>,
 }
 
+#[derive(LintDiagnostic)]
+#[diag(mir_build_non_exhaustive_omitted_pattern_lint_on_arm)]
+#[help]
+pub(crate) struct NonExhaustiveOmittedPatternLintOnArm {
+    #[label]
+    pub lint_span: Span,
+    #[suggestion(code = "#[{lint_level}({lint_name})]\n", applicability = "maybe-incorrect")]
+    pub suggest_lint_on_match: Option<Span>,
+    pub lint_level: &'static str,
+    pub lint_name: &'static str,
+}
+
 #[derive(Subdiagnostic)]
 #[label(mir_build_uncovered)]
 pub(crate) struct Uncovered<'tcx> {
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 933653e708e..8c3d09c19a1 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -9,9 +9,7 @@ use rustc_arena::TypedArena;
 use rustc_ast::Mutability;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::{
-    struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
-};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::*;
 use rustc_hir::def_id::LocalDefId;
@@ -44,7 +42,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err
 
     for param in thir.params.iter() {
         if let Some(box ref pattern) = param.pat {
-            visitor.check_irrefutable(pattern, "function argument", None);
+            visitor.check_binding_is_irrefutable(pattern, "function argument", None);
         }
     }
     visitor.error
@@ -58,7 +56,7 @@ fn create_e0004(
     struct_span_err!(sess, sp, E0004, "{}", &error_message)
 }
 
-#[derive(PartialEq)]
+#[derive(Debug, Copy, Clone, PartialEq)]
 enum RefutableFlag {
     Irrefutable,
     Refutable,
@@ -68,24 +66,30 @@ use RefutableFlag::*;
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 enum LetSource {
     None,
+    PlainLet,
     IfLet,
     IfLetGuard,
     LetElse,
     WhileLet,
 }
 
-struct MatchVisitor<'a, 'p, 'tcx> {
+struct MatchVisitor<'thir, 'p, 'tcx> {
     tcx: TyCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
-    thir: &'a Thir<'tcx>,
+    thir: &'thir Thir<'tcx>,
     lint_level: HirId,
     let_source: LetSource,
     pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
+    /// Tracks if we encountered an error while checking this body. That the first function to
+    /// report it stores it here. Some functions return `Result` to allow callers to short-circuit
+    /// on error, but callers don't need to store it here again.
     error: Result<(), ErrorGuaranteed>,
 }
 
-impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> {
-    fn thir(&self) -> &'a Thir<'tcx> {
+// Visitor for a thir body. This calls `check_match`, `check_let` and `check_let_chain` as
+// appropriate.
+impl<'thir, 'tcx> Visitor<'thir, 'tcx> for MatchVisitor<'thir, '_, 'tcx> {
+    fn thir(&self) -> &'thir Thir<'tcx> {
         self.thir
     }
 
@@ -100,7 +104,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> {
                 }
                 Some(Guard::IfLet(ref pat, expr)) => {
                     this.with_let_source(LetSource::IfLetGuard, |this| {
-                        this.check_let(pat, expr, LetSource::IfLetGuard, pat.span);
+                        this.check_let(pat, Some(expr), pat.span);
                         this.visit_pat(pat);
                         this.visit_expr(&this.thir[expr]);
                     });
@@ -148,10 +152,18 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> {
                 self.check_match(scrutinee, arms, source, ex.span);
             }
             ExprKind::Let { box ref pat, expr } => {
-                self.check_let(pat, expr, self.let_source, ex.span);
+                self.check_let(pat, Some(expr), ex.span);
             }
-            ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
-                self.check_let_chain(self.let_source, ex.span, lhs, rhs);
+            ExprKind::LogicalOp { op: LogicalOp::And, .. }
+                if !matches!(self.let_source, LetSource::None) =>
+            {
+                let mut chain_refutabilities = Vec::new();
+                let Ok(()) = self.visit_land(ex, &mut chain_refutabilities) else { return };
+                // If at least one of the operands is a `let ... = ...`.
+                if chain_refutabilities.iter().any(|x| x.is_some()) {
+                    self.check_let_chain(chain_refutabilities, ex.span);
+                }
+                return;
             }
             _ => {}
         };
@@ -159,33 +171,27 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> {
     }
 
     fn visit_stmt(&mut self, stmt: &Stmt<'tcx>) {
-        let old_lint_level = self.lint_level;
         match stmt.kind {
             StmtKind::Let {
                 box ref pattern, initializer, else_block, lint_level, span, ..
             } => {
-                if let LintLevel::Explicit(lint_level) = lint_level {
-                    self.lint_level = lint_level;
-                }
-
-                if let Some(initializer) = initializer
-                    && else_block.is_some()
-                {
-                    self.check_let(pattern, initializer, LetSource::LetElse, span);
-                }
-
-                if else_block.is_none() {
-                    self.check_irrefutable(pattern, "local binding", Some(span));
-                }
+                self.with_lint_level(lint_level, |this| {
+                    let let_source =
+                        if else_block.is_some() { LetSource::LetElse } else { LetSource::PlainLet };
+                    this.with_let_source(let_source, |this| {
+                        this.check_let(pattern, initializer, span)
+                    });
+                    visit::walk_stmt(this, stmt);
+                });
+            }
+            StmtKind::Expr { .. } => {
+                visit::walk_stmt(self, stmt);
             }
-            _ => {}
         }
-        visit::walk_stmt(self, stmt);
-        self.lint_level = old_lint_level;
     }
 }
 
-impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
+impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
     #[instrument(level = "trace", skip(self, f))]
     fn with_let_source(&mut self, let_source: LetSource, f: impl FnOnce(&mut Self)) {
         let old_let_source = self.let_source;
@@ -194,53 +200,127 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
         self.let_source = old_let_source;
     }
 
-    fn with_lint_level(&mut self, new_lint_level: LintLevel, f: impl FnOnce(&mut Self)) {
+    fn with_lint_level<T>(
+        &mut self,
+        new_lint_level: LintLevel,
+        f: impl FnOnce(&mut Self) -> T,
+    ) -> T {
         if let LintLevel::Explicit(hir_id) = new_lint_level {
             let old_lint_level = self.lint_level;
             self.lint_level = hir_id;
-            f(self);
+            let ret = f(self);
             self.lint_level = old_lint_level;
+            ret
         } else {
-            f(self);
+            f(self)
         }
     }
 
-    fn check_patterns(&self, pat: &Pat<'tcx>, rf: RefutableFlag) {
-        pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
-        check_for_bindings_named_same_as_variants(self, pat, rf);
+    /// Visit a nested chain of `&&`. Used for if-let chains. This must call `visit_expr` on the
+    /// subexpressions we are not handling ourselves.
+    fn visit_land(
+        &mut self,
+        ex: &Expr<'tcx>,
+        accumulator: &mut Vec<Option<(Span, RefutableFlag)>>,
+    ) -> Result<(), ErrorGuaranteed> {
+        match ex.kind {
+            ExprKind::Scope { value, lint_level, .. } => self.with_lint_level(lint_level, |this| {
+                this.visit_land(&this.thir[value], accumulator)
+            }),
+            ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
+                // We recurse into the lhs only, because `&&` chains associate to the left.
+                let res_lhs = self.visit_land(&self.thir[lhs], accumulator);
+                let res_rhs = self.visit_land_rhs(&self.thir[rhs])?;
+                accumulator.push(res_rhs);
+                res_lhs
+            }
+            _ => {
+                let res = self.visit_land_rhs(ex)?;
+                accumulator.push(res);
+                Ok(())
+            }
+        }
+    }
+
+    /// Visit the right-hand-side of a `&&`. Used for if-let chains. Returns `Some` if the
+    /// expression was ultimately a `let ... = ...`, and `None` if it was a normal boolean
+    /// expression. This must call `visit_expr` on the subexpressions we are not handling ourselves.
+    fn visit_land_rhs(
+        &mut self,
+        ex: &Expr<'tcx>,
+    ) -> Result<Option<(Span, RefutableFlag)>, ErrorGuaranteed> {
+        match ex.kind {
+            ExprKind::Scope { value, lint_level, .. } => {
+                self.with_lint_level(lint_level, |this| this.visit_land_rhs(&this.thir[value]))
+            }
+            ExprKind::Let { box ref pat, expr } => {
+                self.with_let_source(LetSource::None, |this| {
+                    this.visit_expr(&this.thir()[expr]);
+                });
+                Ok(Some((ex.span, self.is_let_irrefutable(pat)?)))
+            }
+            _ => {
+                self.with_let_source(LetSource::None, |this| {
+                    this.visit_expr(ex);
+                });
+                Ok(None)
+            }
+        }
     }
 
     fn lower_pattern(
-        &self,
-        cx: &mut MatchCheckCtxt<'p, 'tcx>,
-        pattern: &Pat<'tcx>,
-    ) -> &'p DeconstructedPat<'p, 'tcx> {
-        cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, &pattern))
+        &mut self,
+        cx: &MatchCheckCtxt<'p, 'tcx>,
+        pat: &Pat<'tcx>,
+    ) -> Result<&'p DeconstructedPat<'p, 'tcx>, ErrorGuaranteed> {
+        if let Err(err) = pat.pat_error_reported() {
+            self.error = Err(err);
+            Err(err)
+        } else {
+            // Check the pattern for some things unrelated to exhaustiveness.
+            let refutable = if cx.refutable { Refutable } else { Irrefutable };
+            pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
+            pat.walk_always(|pat| check_for_bindings_named_same_as_variants(self, pat, refutable));
+            Ok(cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, pat)))
+        }
     }
 
-    fn new_cx(&self, hir_id: HirId, refutable: bool) -> MatchCheckCtxt<'p, 'tcx> {
+    fn new_cx(
+        &self,
+        refutability: RefutableFlag,
+        match_span: Option<Span>,
+    ) -> MatchCheckCtxt<'p, 'tcx> {
+        let refutable = match refutability {
+            Irrefutable => false,
+            Refutable => true,
+        };
         MatchCheckCtxt {
             tcx: self.tcx,
             param_env: self.param_env,
-            module: self.tcx.parent_module(hir_id).to_def_id(),
+            module: self.tcx.parent_module(self.lint_level).to_def_id(),
             pattern_arena: &self.pattern_arena,
+            match_span,
             refutable,
         }
     }
 
     #[instrument(level = "trace", skip(self))]
-    fn check_let(&mut self, pat: &Pat<'tcx>, scrutinee: ExprId, source: LetSource, span: Span) {
-        if let LetSource::None = source {
-            return;
-        }
-        if let Err(err) = pat.pat_error_reported() {
-            self.error = Err(err);
-            return;
+    fn check_let(&mut self, pat: &Pat<'tcx>, scrutinee: Option<ExprId>, span: Span) {
+        assert!(self.let_source != LetSource::None);
+        if let LetSource::PlainLet = self.let_source {
+            self.check_binding_is_irrefutable(pat, "local binding", Some(span))
+        } else {
+            let Ok(refutability) = self.is_let_irrefutable(pat) else { return };
+            if matches!(refutability, Irrefutable) {
+                report_irrefutable_let_patterns(
+                    self.tcx,
+                    self.lint_level,
+                    self.let_source,
+                    1,
+                    span,
+                );
+            }
         }
-        self.check_patterns(pat, Refutable);
-        let mut cx = self.new_cx(self.lint_level, true);
-        let tpat = self.lower_pattern(&mut cx, pat);
-        self.check_let_reachability(&mut cx, self.lint_level, source, tpat, span);
     }
 
     fn check_match(
@@ -250,33 +330,22 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
         source: hir::MatchSource,
         expr_span: Span,
     ) {
-        let mut cx = self.new_cx(self.lint_level, true);
+        let cx = self.new_cx(Refutable, Some(expr_span));
 
+        let mut tarms = Vec::with_capacity(arms.len());
         for &arm in arms {
-            // Check the arm for some things unrelated to exhaustiveness.
             let arm = &self.thir.arms[arm];
-            self.with_lint_level(arm.lint_level, |this| {
-                this.check_patterns(&arm.pattern, Refutable);
+            let got_error = self.with_lint_level(arm.lint_level, |this| {
+                let Ok(pat) = this.lower_pattern(&cx, &arm.pattern) else { return true };
+                let arm = MatchArm { pat, hir_id: this.lint_level, has_guard: arm.guard.is_some() };
+                tarms.push(arm);
+                false
             });
-            if let Err(err) = arm.pattern.pat_error_reported() {
-                self.error = Err(err);
+            if got_error {
                 return;
             }
         }
 
-        let tarms: Vec<_> = arms
-            .iter()
-            .map(|&arm| {
-                let arm = &self.thir.arms[arm];
-                let hir_id = match arm.lint_level {
-                    LintLevel::Explicit(hir_id) => hir_id,
-                    LintLevel::Inherited => self.lint_level,
-                };
-                let pat = self.lower_pattern(&mut cx, &arm.pattern);
-                MatchArm { pat, hir_id, has_guard: arm.guard.is_some() }
-            })
-            .collect();
-
         let scrut = &self.thir[scrut];
         let scrut_ty = scrut.ty;
         let report = compute_match_usefulness(&cx, &tarms, self.lint_level, scrut_ty, scrut.span);
@@ -303,118 +372,37 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
                 debug_assert_eq!(pat.span.desugaring_kind(), Some(DesugaringKind::ForLoop));
                 let PatKind::Variant { ref subpatterns, .. } = pat.kind else { bug!() };
                 let [pat_field] = &subpatterns[..] else { bug!() };
-                self.check_irrefutable(&pat_field.pattern, "`for` loop binding", None);
+                self.check_binding_is_irrefutable(&pat_field.pattern, "`for` loop binding", None);
             } else {
-                self.error = Err(non_exhaustive_match(
+                self.error = Err(report_non_exhaustive_match(
                     &cx, self.thir, scrut_ty, scrut.span, witnesses, arms, expr_span,
                 ));
             }
         }
     }
 
-    fn check_let_reachability(
-        &mut self,
-        cx: &mut MatchCheckCtxt<'p, 'tcx>,
-        pat_id: HirId,
-        source: LetSource,
-        pat: &'p DeconstructedPat<'p, 'tcx>,
-        span: Span,
-    ) {
-        if is_let_irrefutable(cx, pat_id, pat) {
-            irrefutable_let_patterns(cx.tcx, pat_id, source, 1, span);
-        }
-    }
-
     #[instrument(level = "trace", skip(self))]
     fn check_let_chain(
         &mut self,
-        let_source: LetSource,
-        top_expr_span: Span,
-        mut lhs: ExprId,
-        rhs: ExprId,
+        chain_refutabilities: Vec<Option<(Span, RefutableFlag)>>,
+        whole_chain_span: Span,
     ) {
-        if let LetSource::None = let_source {
-            return;
-        }
-
-        // Lint level enclosing the next `lhs`.
-        let mut cur_lint_level = self.lint_level;
-
-        // Obtain the refutabilities of all exprs in the chain,
-        // and record chain members that aren't let exprs.
-        let mut chain_refutabilities = Vec::new();
-
-        let mut error = Ok(());
-        let mut add = |expr: ExprId, mut local_lint_level| {
-            // `local_lint_level` is the lint level enclosing the pattern inside `expr`.
-            let mut expr = &self.thir[expr];
-            debug!(?expr, ?local_lint_level, "add");
-            // Fast-forward through scopes.
-            while let ExprKind::Scope { value, lint_level, .. } = expr.kind {
-                if let LintLevel::Explicit(hir_id) = lint_level {
-                    local_lint_level = hir_id
-                }
-                expr = &self.thir[value];
-            }
-            debug!(?expr, ?local_lint_level, "after scopes");
-            match expr.kind {
-                ExprKind::Let { box ref pat, expr: _ } => {
-                    if let Err(err) = pat.pat_error_reported() {
-                        error = Err(err);
-                        return None;
-                    }
-                    let mut ncx = self.new_cx(local_lint_level, true);
-                    let tpat = self.lower_pattern(&mut ncx, pat);
-                    let refutable = !is_let_irrefutable(&mut ncx, local_lint_level, tpat);
-                    Some((expr.span, refutable))
-                }
-                _ => None,
-            }
-        };
-
-        // Let chains recurse on the left, so we start by adding the rightmost.
-        chain_refutabilities.push(add(rhs, cur_lint_level));
+        assert!(self.let_source != LetSource::None);
 
-        loop {
-            while let ExprKind::Scope { value, lint_level, .. } = self.thir[lhs].kind {
-                if let LintLevel::Explicit(hir_id) = lint_level {
-                    cur_lint_level = hir_id
-                }
-                lhs = value;
-            }
-            if let ExprKind::LogicalOp { op: LogicalOp::And, lhs: new_lhs, rhs: expr } =
-                self.thir[lhs].kind
-            {
-                chain_refutabilities.push(add(expr, cur_lint_level));
-                lhs = new_lhs;
-            } else {
-                chain_refutabilities.push(add(lhs, cur_lint_level));
-                break;
-            }
-        }
-        debug!(?chain_refutabilities);
-        chain_refutabilities.reverse();
-
-        if error.is_err() {
-            self.error = error;
-            return;
-        }
-
-        // Third, emit the actual warnings.
-        if chain_refutabilities.iter().all(|r| matches!(*r, Some((_, false)))) {
+        if chain_refutabilities.iter().all(|r| matches!(*r, Some((_, Irrefutable)))) {
             // The entire chain is made up of irrefutable `let` statements
-            irrefutable_let_patterns(
+            report_irrefutable_let_patterns(
                 self.tcx,
                 self.lint_level,
-                let_source,
+                self.let_source,
                 chain_refutabilities.len(),
-                top_expr_span,
+                whole_chain_span,
             );
             return;
         }
 
         if let Some(until) =
-            chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, false))))
+            chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, Irrefutable))))
             && until > 0
         {
             // The chain has a non-zero prefix of irrefutable `let` statements.
@@ -425,7 +413,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
             // so can't always be moved out.
             // FIXME: Add checking whether the bindings are actually used in the prefix,
             // and lint if they are not.
-            if !matches!(let_source, LetSource::WhileLet | LetSource::IfLetGuard) {
+            if !matches!(self.let_source, LetSource::WhileLet | LetSource::IfLetGuard) {
                 // Emit the lint
                 let prefix = &chain_refutabilities[..until];
                 let span_start = prefix[0].unwrap().0;
@@ -442,7 +430,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
         }
 
         if let Some(from) =
-            chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, false))))
+            chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, Irrefutable))))
             && from != (chain_refutabilities.len() - 1)
         {
             // The chain has a non-empty suffix of irrefutable `let` statements
@@ -460,28 +448,36 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
         }
     }
 
-    #[instrument(level = "trace", skip(self))]
-    fn check_irrefutable(&mut self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) {
-        // If we got errors while lowering, don't emit anything more.
-        if let Err(err) = pat.pat_error_reported() {
-            self.error = Err(err);
-            return;
-        }
+    fn analyze_binding(
+        &mut self,
+        pat: &Pat<'tcx>,
+        refutability: RefutableFlag,
+    ) -> Result<(MatchCheckCtxt<'p, 'tcx>, UsefulnessReport<'p, 'tcx>), ErrorGuaranteed> {
+        let cx = self.new_cx(refutability, None);
+        let pat = self.lower_pattern(&cx, pat)?;
+        let arms = [MatchArm { pat, hir_id: self.lint_level, has_guard: false }];
+        let report = compute_match_usefulness(&cx, &arms, self.lint_level, pat.ty(), pat.span());
+        Ok((cx, report))
+    }
 
-        let mut cx = self.new_cx(self.lint_level, false);
+    fn is_let_irrefutable(&mut self, pat: &Pat<'tcx>) -> Result<RefutableFlag, ErrorGuaranteed> {
+        let (cx, report) = self.analyze_binding(pat, Refutable)?;
+        // Report if the pattern is unreachable, which can only occur when the type is uninhabited.
+        // This also reports unreachable sub-patterns.
+        report_arm_reachability(&cx, &report);
+        // If the list of witnesses is empty, the match is exhaustive, i.e. the `if let` pattern is
+        // irrefutable.
+        Ok(if report.non_exhaustiveness_witnesses.is_empty() { Irrefutable } else { Refutable })
+    }
 
-        let pattern = self.lower_pattern(&mut cx, pat);
-        let pattern_ty = pattern.ty();
-        let arm = MatchArm { pat: pattern, hir_id: self.lint_level, has_guard: false };
-        let report =
-            compute_match_usefulness(&cx, &[arm], self.lint_level, pattern_ty, pattern.span());
+    #[instrument(level = "trace", skip(self))]
+    fn check_binding_is_irrefutable(&mut self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) {
+        let pattern_ty = pat.ty;
 
-        // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We
-        // only care about exhaustiveness here.
+        let Ok((cx, report)) = self.analyze_binding(pat, Irrefutable) else { return };
         let witnesses = report.non_exhaustiveness_witnesses;
         if witnesses.is_empty() {
             // The pattern is irrefutable.
-            self.check_patterns(pat, Irrefutable);
             return;
         }
 
@@ -528,30 +524,20 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
             });
         };
 
-        let adt_defined_here = try {
-            let ty = pattern_ty.peel_refs();
-            let ty::Adt(def, _) = ty.kind() else { None? };
-            let adt_def_span = cx.tcx.hir().get_if_local(def.did())?.ident()?.span;
-            let mut variants = vec![];
-
-            for span in maybe_point_at_variant(&cx, *def, witnesses.iter().take(5)) {
-                variants.push(Variant { span });
-            }
-            AdtDefinedHere { adt_def_span, ty, variants }
-        };
+        let adt_defined_here = report_adt_defined_here(self.tcx, pattern_ty, &witnesses, false);
 
         // Emit an extra note if the first uncovered witness would be uninhabited
         // if we disregard visibility.
-        let witness_1_is_privately_uninhabited = if cx.tcx.features().exhaustive_patterns
+        let witness_1_is_privately_uninhabited = if self.tcx.features().exhaustive_patterns
             && let Some(witness_1) = witnesses.get(0)
             && let ty::Adt(adt, args) = witness_1.ty().kind()
             && adt.is_enum()
             && let Constructor::Variant(variant_index) = witness_1.ctor()
         {
             let variant = adt.variant(*variant_index);
-            let inhabited = variant.inhabited_predicate(cx.tcx, *adt).instantiate(cx.tcx, args);
-            assert!(inhabited.apply(cx.tcx, cx.param_env, cx.module));
-            !inhabited.apply_ignore_module(cx.tcx, cx.param_env)
+            let inhabited = variant.inhabited_predicate(self.tcx, *adt).instantiate(self.tcx, args);
+            assert!(inhabited.apply(self.tcx, cx.param_env, cx.module));
+            !inhabited.apply_ignore_module(self.tcx, cx.param_env)
         } else {
             false
         };
@@ -572,70 +558,154 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
     }
 }
 
-fn check_for_bindings_named_same_as_variants(
-    cx: &MatchVisitor<'_, '_, '_>,
-    pat: &Pat<'_>,
-    rf: RefutableFlag,
-) {
-    pat.walk_always(|p| {
-        if let PatKind::Binding {
-            name,
-            mode: BindingMode::ByValue,
-            mutability: Mutability::Not,
-            subpattern: None,
-            ty,
-            ..
-        } = p.kind
-            && let ty::Adt(edef, _) = ty.peel_refs().kind()
-            && edef.is_enum()
-            && edef
-                .variants()
-                .iter()
-                .any(|variant| variant.name == name && variant.ctor_kind() == Some(CtorKind::Const))
-        {
-            let variant_count = edef.variants().len();
-            let ty_path = with_no_trimmed_paths!(cx.tcx.def_path_str(edef.did()));
-            cx.tcx.emit_spanned_lint(
-                BINDINGS_WITH_VARIANT_NAME,
-                cx.lint_level,
-                p.span,
-                BindingsWithVariantName {
-                    // If this is an irrefutable pattern, and there's > 1 variant,
-                    // then we can't actually match on this. Applying the below
-                    // suggestion would produce code that breaks on `check_irrefutable`.
-                    suggestion: if rf == Refutable || variant_count == 1 {
-                        Some(p.span)
-                    } else {
-                        None
-                    },
-                    ty_path,
+/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
+/// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
+///
+/// For example, this would reject:
+/// - `ref x @ Some(ref mut y)`,
+/// - `ref mut x @ Some(ref y)`,
+/// - `ref mut x @ Some(ref mut y)`,
+/// - `ref mut? x @ Some(y)`, and
+/// - `x @ Some(ref mut? y)`.
+///
+/// This analysis is *not* subsumed by NLL.
+fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, '_, 'tcx>, pat: &Pat<'tcx>) {
+    // Extract `sub` in `binding @ sub`.
+    let PatKind::Binding { name, mode, ty, subpattern: Some(box ref sub), .. } = pat.kind else {
+        return;
+    };
+
+    let is_binding_by_move = |ty: Ty<'tcx>| !ty.is_copy_modulo_regions(cx.tcx, cx.param_env);
+
+    let sess = cx.tcx.sess;
+
+    // Get the binding move, extract the mutability if by-ref.
+    let mut_outer = match mode {
+        BindingMode::ByValue if is_binding_by_move(ty) => {
+            // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
+            let mut conflicts_ref = Vec::new();
+            sub.each_binding(|_, mode, _, span| match mode {
+                BindingMode::ByValue => {}
+                BindingMode::ByRef(_) => conflicts_ref.push(span),
+            });
+            if !conflicts_ref.is_empty() {
+                sess.emit_err(BorrowOfMovedValue {
+                    binding_span: pat.span,
+                    conflicts_ref,
                     name,
-                },
-            )
+                    ty,
+                    suggest_borrowing: Some(pat.span.shrink_to_lo()),
+                });
+            }
+            return;
+        }
+        BindingMode::ByValue => return,
+        BindingMode::ByRef(m) => m.mutability(),
+    };
+
+    // We now have `ref $mut_outer binding @ sub` (semantically).
+    // Recurse into each binding in `sub` and find mutability or move conflicts.
+    let mut conflicts_move = Vec::new();
+    let mut conflicts_mut_mut = Vec::new();
+    let mut conflicts_mut_ref = Vec::new();
+    sub.each_binding(|name, mode, ty, span| {
+        match mode {
+            BindingMode::ByRef(mut_inner) => match (mut_outer, mut_inner.mutability()) {
+                // Both sides are `ref`.
+                (Mutability::Not, Mutability::Not) => {}
+                // 2x `ref mut`.
+                (Mutability::Mut, Mutability::Mut) => {
+                    conflicts_mut_mut.push(Conflict::Mut { span, name })
+                }
+                (Mutability::Not, Mutability::Mut) => {
+                    conflicts_mut_ref.push(Conflict::Mut { span, name })
+                }
+                (Mutability::Mut, Mutability::Not) => {
+                    conflicts_mut_ref.push(Conflict::Ref { span, name })
+                }
+            },
+            BindingMode::ByValue if is_binding_by_move(ty) => {
+                conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict.
+            }
+            BindingMode::ByValue => {} // `ref mut?` + by-copy is fine.
         }
     });
-}
 
-/// Checks for common cases of "catchall" patterns that may not be intended as such.
-fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
-    use Constructor::*;
-    match pat.ctor() {
-        Wildcard => true,
-        Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
-        _ => false,
+    let report_mut_mut = !conflicts_mut_mut.is_empty();
+    let report_mut_ref = !conflicts_mut_ref.is_empty();
+    let report_move_conflict = !conflicts_move.is_empty();
+
+    let mut occurrences = match mut_outer {
+        Mutability::Mut => vec![Conflict::Mut { span: pat.span, name }],
+        Mutability::Not => vec![Conflict::Ref { span: pat.span, name }],
+    };
+    occurrences.extend(conflicts_mut_mut);
+    occurrences.extend(conflicts_mut_ref);
+    occurrences.extend(conflicts_move);
+
+    // Report errors if any.
+    if report_mut_mut {
+        // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
+        sess.emit_err(MultipleMutBorrows { span: pat.span, occurrences });
+    } else if report_mut_ref {
+        // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
+        match mut_outer {
+            Mutability::Mut => {
+                sess.emit_err(AlreadyMutBorrowed { span: pat.span, occurrences });
+            }
+            Mutability::Not => {
+                sess.emit_err(AlreadyBorrowed { span: pat.span, occurrences });
+            }
+        };
+    } else if report_move_conflict {
+        // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
+        sess.emit_err(MovedWhileBorrowed { span: pat.span, occurrences });
     }
 }
 
-fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
-    tcx.emit_spanned_lint(
-        UNREACHABLE_PATTERNS,
-        id,
-        span,
-        UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall },
-    );
+fn check_for_bindings_named_same_as_variants(
+    cx: &MatchVisitor<'_, '_, '_>,
+    pat: &Pat<'_>,
+    rf: RefutableFlag,
+) {
+    if let PatKind::Binding {
+        name,
+        mode: BindingMode::ByValue,
+        mutability: Mutability::Not,
+        subpattern: None,
+        ty,
+        ..
+    } = pat.kind
+        && let ty::Adt(edef, _) = ty.peel_refs().kind()
+        && edef.is_enum()
+        && edef
+            .variants()
+            .iter()
+            .any(|variant| variant.name == name && variant.ctor_kind() == Some(CtorKind::Const))
+    {
+        let variant_count = edef.variants().len();
+        let ty_path = with_no_trimmed_paths!(cx.tcx.def_path_str(edef.did()));
+        cx.tcx.emit_spanned_lint(
+            BINDINGS_WITH_VARIANT_NAME,
+            cx.lint_level,
+            pat.span,
+            BindingsWithVariantName {
+                // If this is an irrefutable pattern, and there's > 1 variant,
+                // then we can't actually match on this. Applying the below
+                // suggestion would produce code that breaks on `check_binding_is_irrefutable`.
+                suggestion: if rf == Refutable || variant_count == 1 {
+                    Some(pat.span)
+                } else {
+                    None
+                },
+                ty_path,
+                name,
+            },
+        )
+    }
 }
 
-fn irrefutable_let_patterns(
+fn report_irrefutable_let_patterns(
     tcx: TyCtxt<'_>,
     id: HirId,
     source: LetSource,
@@ -649,7 +719,7 @@ fn irrefutable_let_patterns(
     }
 
     match source {
-        LetSource::None => bug!(),
+        LetSource::None | LetSource::PlainLet => bug!(),
         LetSource::IfLet => emit_diag!(IrrefutableLetPatternsIfLet),
         LetSource::IfLetGuard => emit_diag!(IrrefutableLetPatternsIfLetGuard),
         LetSource::LetElse => emit_diag!(IrrefutableLetPatternsLetElse),
@@ -657,34 +727,28 @@ fn irrefutable_let_patterns(
     }
 }
 
-fn is_let_irrefutable<'p, 'tcx>(
-    cx: &mut MatchCheckCtxt<'p, 'tcx>,
-    pat_id: HirId,
-    pat: &'p DeconstructedPat<'p, 'tcx>,
-) -> bool {
-    let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }];
-    let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty(), pat.span());
-
-    // Report if the pattern is unreachable, which can only occur when the type is uninhabited.
-    // This also reports unreachable sub-patterns though, so we can't just replace it with an
-    // `is_uninhabited` check.
-    report_arm_reachability(&cx, &report);
-
-    // If the list of witnesses is empty, the match is exhaustive,
-    // i.e. the `if let` pattern is irrefutable.
-    report.non_exhaustiveness_witnesses.is_empty()
-}
-
 /// Report unreachable arms, if any.
 fn report_arm_reachability<'p, 'tcx>(
     cx: &MatchCheckCtxt<'p, 'tcx>,
     report: &UsefulnessReport<'p, 'tcx>,
 ) {
+    let report_unreachable_pattern = |span, hir_id, catchall: Option<Span>| {
+        cx.tcx.emit_spanned_lint(
+            UNREACHABLE_PATTERNS,
+            hir_id,
+            span,
+            UnreachablePattern {
+                span: if catchall.is_some() { Some(span) } else { None },
+                catchall,
+            },
+        );
+    };
+
     use Reachability::*;
     let mut catchall = None;
     for (arm, is_useful) in report.arm_usefulness.iter() {
         match is_useful {
-            Unreachable => unreachable_pattern(cx.tcx, arm.pat.span(), arm.hir_id, catchall),
+            Unreachable => report_unreachable_pattern(arm.pat.span(), arm.hir_id, catchall),
             Reachable(unreachables) if unreachables.is_empty() => {}
             // The arm is reachable, but contains unreachable subpatterns (from or-patterns).
             Reachable(unreachables) => {
@@ -692,7 +756,7 @@ fn report_arm_reachability<'p, 'tcx>(
                 // Emit lints in the order in which they occur in the file.
                 unreachables.sort_unstable();
                 for span in unreachables {
-                    unreachable_pattern(cx.tcx, span, arm.hir_id, None);
+                    report_unreachable_pattern(span, arm.hir_id, None);
                 }
             }
         }
@@ -702,26 +766,18 @@ fn report_arm_reachability<'p, 'tcx>(
     }
 }
 
-fn collect_non_exhaustive_tys<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    pat: &WitnessPat<'tcx>,
-    non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
-) {
-    if matches!(pat.ctor(), Constructor::NonExhaustive) {
-        non_exhaustive_tys.insert(pat.ty());
-    }
-    if let Constructor::IntRange(range) = pat.ctor() {
-        if range.is_beyond_boundaries(pat.ty(), tcx) {
-            // The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
-            non_exhaustive_tys.insert(pat.ty());
-        }
+/// Checks for common cases of "catchall" patterns that may not be intended as such.
+fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
+    use Constructor::*;
+    match pat.ctor() {
+        Wildcard => true,
+        Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
+        _ => false,
     }
-    pat.iter_fields()
-        .for_each(|field_pat| collect_non_exhaustive_tys(tcx, field_pat, non_exhaustive_tys))
 }
 
 /// Report that a match is not exhaustive.
-fn non_exhaustive_match<'p, 'tcx>(
+fn report_non_exhaustive_match<'p, 'tcx>(
     cx: &MatchCheckCtxt<'p, 'tcx>,
     thir: &Thir<'tcx>,
     scrut_ty: Ty<'tcx>,
@@ -755,7 +811,14 @@ fn non_exhaustive_match<'p, 'tcx>(
             sp,
             format!("non-exhaustive patterns: {joined_patterns} not covered"),
         );
-        err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
+        err.span_label(
+            sp,
+            format!(
+                "pattern{} {} not covered",
+                rustc_errors::pluralize!(witnesses.len()),
+                joined_patterns
+            ),
+        );
         patterns_len = witnesses.len();
         pattern = if witnesses.len() < 4 {
             witnesses
@@ -768,7 +831,17 @@ fn non_exhaustive_match<'p, 'tcx>(
         };
     };
 
-    adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
+    // Point at the definition of non-covered `enum` variants.
+    if let Some(AdtDefinedHere { adt_def_span, ty, variants }) =
+        report_adt_defined_here(cx.tcx, scrut_ty, &witnesses, true)
+    {
+        let mut multi_span = MultiSpan::from_span(adt_def_span);
+        multi_span.push_span_label(adt_def_span, "");
+        for Variant { span } in variants {
+            multi_span.push_span_label(span, "not covered");
+        }
+        err.span_note(multi_span, format!("`{ty}` defined here"));
+    }
     err.note(format!("the matched value is of type `{}`", scrut_ty));
 
     if !is_empty_match {
@@ -910,7 +983,7 @@ fn non_exhaustive_match<'p, 'tcx>(
     err.emit()
 }
 
-pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
+fn joined_uncovered_patterns<'p, 'tcx>(
     cx: &MatchCheckCtxt<'p, 'tcx>,
     witnesses: &[WitnessPat<'tcx>],
 ) -> String {
@@ -931,48 +1004,51 @@ pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
     }
 }
 
-pub(crate) fn pattern_not_covered_label(
-    witnesses: &[WitnessPat<'_>],
-    joined_patterns: &str,
-) -> String {
-    format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
+fn collect_non_exhaustive_tys<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    pat: &WitnessPat<'tcx>,
+    non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
+) {
+    if matches!(pat.ctor(), Constructor::NonExhaustive) {
+        non_exhaustive_tys.insert(pat.ty());
+    }
+    if let Constructor::IntRange(range) = pat.ctor() {
+        if range.is_beyond_boundaries(pat.ty(), tcx) {
+            // The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
+            non_exhaustive_tys.insert(pat.ty());
+        }
+    }
+    pat.iter_fields()
+        .for_each(|field_pat| collect_non_exhaustive_tys(tcx, field_pat, non_exhaustive_tys))
 }
 
-/// Point at the definition of non-covered `enum` variants.
-fn adt_defined_here<'p, 'tcx>(
-    cx: &MatchCheckCtxt<'p, 'tcx>,
-    err: &mut Diagnostic,
+fn report_adt_defined_here<'tcx>(
+    tcx: TyCtxt<'tcx>,
     ty: Ty<'tcx>,
     witnesses: &[WitnessPat<'tcx>],
-) {
+    point_at_non_local_ty: bool,
+) -> Option<AdtDefinedHere<'tcx>> {
     let ty = ty.peel_refs();
-    if let ty::Adt(def, _) = ty.kind() {
-        let mut spans = vec![];
-        if witnesses.len() < 5 {
-            for sp in maybe_point_at_variant(cx, *def, witnesses.iter()) {
-                spans.push(sp);
-            }
-        }
-        let def_span = cx
-            .tcx
-            .hir()
-            .get_if_local(def.did())
-            .and_then(|node| node.ident())
-            .map(|ident| ident.span)
-            .unwrap_or_else(|| cx.tcx.def_span(def.did()));
-        let mut span: MultiSpan =
-            if spans.is_empty() { def_span.into() } else { spans.clone().into() };
-
-        span.push_span_label(def_span, "");
-        for pat in spans {
-            span.push_span_label(pat, "not covered");
-        }
-        err.span_note(span, format!("`{ty}` defined here"));
+    let ty::Adt(def, _) = ty.kind() else {
+        return None;
+    };
+    let adt_def_span =
+        tcx.hir().get_if_local(def.did()).and_then(|node| node.ident()).map(|ident| ident.span);
+    let adt_def_span = if point_at_non_local_ty {
+        adt_def_span.unwrap_or_else(|| tcx.def_span(def.did()))
+    } else {
+        adt_def_span?
+    };
+
+    let mut variants = vec![];
+    for span in maybe_point_at_variant(tcx, *def, witnesses.iter().take(5)) {
+        variants.push(Variant { span });
     }
+    Some(AdtDefinedHere { adt_def_span, ty, variants })
 }
 
-fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
-    cx: &MatchCheckCtxt<'p, 'tcx>,
+fn maybe_point_at_variant<'a, 'tcx: 'a>(
+    tcx: TyCtxt<'tcx>,
     def: AdtDef<'tcx>,
     patterns: impl Iterator<Item = &'a WitnessPat<'tcx>>,
 ) -> Vec<Span> {
@@ -985,7 +1061,7 @@ fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
             {
                 continue;
             }
-            let sp = def.variant(*variant_index).ident(cx.tcx).span;
+            let sp = def.variant(*variant_index).ident(tcx).span;
             if covered.contains(&sp) {
                 // Don't point at variants that have already been covered due to other patterns to avoid
                 // visual clutter.
@@ -993,112 +1069,7 @@ fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
             }
             covered.push(sp);
         }
-        covered.extend(maybe_point_at_variant(cx, def, pattern.iter_fields()));
+        covered.extend(maybe_point_at_variant(tcx, def, pattern.iter_fields()));
     }
     covered
 }
-
-/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
-/// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
-///
-/// For example, this would reject:
-/// - `ref x @ Some(ref mut y)`,
-/// - `ref mut x @ Some(ref y)`,
-/// - `ref mut x @ Some(ref mut y)`,
-/// - `ref mut? x @ Some(y)`, and
-/// - `x @ Some(ref mut? y)`.
-///
-/// This analysis is *not* subsumed by NLL.
-fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, '_, 'tcx>, pat: &Pat<'tcx>) {
-    // Extract `sub` in `binding @ sub`.
-    let PatKind::Binding { name, mode, ty, subpattern: Some(box ref sub), .. } = pat.kind else {
-        return;
-    };
-
-    let is_binding_by_move = |ty: Ty<'tcx>| !ty.is_copy_modulo_regions(cx.tcx, cx.param_env);
-
-    let sess = cx.tcx.sess;
-
-    // Get the binding move, extract the mutability if by-ref.
-    let mut_outer = match mode {
-        BindingMode::ByValue if is_binding_by_move(ty) => {
-            // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
-            let mut conflicts_ref = Vec::new();
-            sub.each_binding(|_, mode, _, span| match mode {
-                BindingMode::ByValue => {}
-                BindingMode::ByRef(_) => conflicts_ref.push(span),
-            });
-            if !conflicts_ref.is_empty() {
-                sess.emit_err(BorrowOfMovedValue {
-                    binding_span: pat.span,
-                    conflicts_ref,
-                    name,
-                    ty,
-                    suggest_borrowing: Some(pat.span.shrink_to_lo()),
-                });
-            }
-            return;
-        }
-        BindingMode::ByValue => return,
-        BindingMode::ByRef(m) => m.mutability(),
-    };
-
-    // We now have `ref $mut_outer binding @ sub` (semantically).
-    // Recurse into each binding in `sub` and find mutability or move conflicts.
-    let mut conflicts_move = Vec::new();
-    let mut conflicts_mut_mut = Vec::new();
-    let mut conflicts_mut_ref = Vec::new();
-    sub.each_binding(|name, mode, ty, span| {
-        match mode {
-            BindingMode::ByRef(mut_inner) => match (mut_outer, mut_inner.mutability()) {
-                // Both sides are `ref`.
-                (Mutability::Not, Mutability::Not) => {}
-                // 2x `ref mut`.
-                (Mutability::Mut, Mutability::Mut) => {
-                    conflicts_mut_mut.push(Conflict::Mut { span, name })
-                }
-                (Mutability::Not, Mutability::Mut) => {
-                    conflicts_mut_ref.push(Conflict::Mut { span, name })
-                }
-                (Mutability::Mut, Mutability::Not) => {
-                    conflicts_mut_ref.push(Conflict::Ref { span, name })
-                }
-            },
-            BindingMode::ByValue if is_binding_by_move(ty) => {
-                conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict.
-            }
-            BindingMode::ByValue => {} // `ref mut?` + by-copy is fine.
-        }
-    });
-
-    let report_mut_mut = !conflicts_mut_mut.is_empty();
-    let report_mut_ref = !conflicts_mut_ref.is_empty();
-    let report_move_conflict = !conflicts_move.is_empty();
-
-    let mut occurrences = match mut_outer {
-        Mutability::Mut => vec![Conflict::Mut { span: pat.span, name }],
-        Mutability::Not => vec![Conflict::Ref { span: pat.span, name }],
-    };
-    occurrences.extend(conflicts_mut_mut);
-    occurrences.extend(conflicts_mut_ref);
-    occurrences.extend(conflicts_move);
-
-    // Report errors if any.
-    if report_mut_mut {
-        // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
-        sess.emit_err(MultipleMutBorrows { span: pat.span, occurrences });
-    } else if report_mut_ref {
-        // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
-        match mut_outer {
-            Mutability::Mut => {
-                sess.emit_err(AlreadyMutBorrowed { span: pat.span, occurrences });
-            }
-            Mutability::Not => {
-                sess.emit_err(AlreadyBorrowed { span: pat.span, occurrences });
-            }
-        };
-    } else if report_move_conflict {
-        // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
-        sess.emit_err(MovedWhileBorrowed { span: pat.span, occurrences });
-    }
-}
diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
index 1eb1dd72a61..da7b6587a72 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
@@ -311,7 +311,10 @@ use super::deconstruct_pat::{
     Constructor, ConstructorSet, DeconstructedPat, IntRange, MaybeInfiniteInt, SplitConstructorSet,
     WitnessPat,
 };
-use crate::errors::{NonExhaustiveOmittedPattern, Overlap, OverlappingRangeEndpoints, Uncovered};
+use crate::errors::{
+    NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Overlap,
+    OverlappingRangeEndpoints, Uncovered,
+};
 
 use rustc_data_structures::captures::Captures;
 
@@ -337,6 +340,8 @@ pub(crate) struct MatchCheckCtxt<'p, 'tcx> {
     pub(crate) module: DefId,
     pub(crate) param_env: ty::ParamEnv<'tcx>,
     pub(crate) pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
+    /// The span of the whole match, if applicable.
+    pub(crate) match_span: Option<Span>,
     /// Only produce `NON_EXHAUSTIVE_OMITTED_PATTERNS` lint on refutable patterns.
     pub(crate) refutable: bool,
 }
@@ -1149,28 +1154,50 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
 
     // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
     // `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
-    if cx.refutable
-        && non_exhaustiveness_witnesses.is_empty()
-        && !matches!(
+    if cx.refutable && non_exhaustiveness_witnesses.is_empty() {
+        if !matches!(
             cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, lint_root).0,
             rustc_session::lint::Level::Allow
-        )
-    {
-        let witnesses = collect_nonexhaustive_missing_variants(cx, &pat_column);
-        if !witnesses.is_empty() {
-            // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
-            // is not exhaustive enough.
-            //
-            // NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`.
-            cx.tcx.emit_spanned_lint(
-                NON_EXHAUSTIVE_OMITTED_PATTERNS,
-                lint_root,
-                scrut_span,
-                NonExhaustiveOmittedPattern {
-                    scrut_ty,
-                    uncovered: Uncovered::new(scrut_span, cx, witnesses),
-                },
-            );
+        ) {
+            let witnesses = collect_nonexhaustive_missing_variants(cx, &pat_column);
+
+            if !witnesses.is_empty() {
+                // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
+                // is not exhaustive enough.
+                //
+                // NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`.
+                cx.tcx.emit_spanned_lint(
+                    NON_EXHAUSTIVE_OMITTED_PATTERNS,
+                    lint_root,
+                    scrut_span,
+                    NonExhaustiveOmittedPattern {
+                        scrut_ty,
+                        uncovered: Uncovered::new(scrut_span, cx, witnesses),
+                    },
+                );
+            }
+        } else {
+            // We used to allow putting the `#[allow(non_exhaustive_omitted_patterns)]` on a match
+            // arm. This no longer makes sense so we warn users, to avoid silently breaking their
+            // usage of the lint.
+            for arm in arms {
+                let (lint_level, lint_level_source) =
+                    cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.hir_id);
+                if !matches!(lint_level, rustc_session::lint::Level::Allow) {
+                    let decorator = NonExhaustiveOmittedPatternLintOnArm {
+                        lint_span: lint_level_source.span(),
+                        suggest_lint_on_match: cx.match_span.map(|span| span.shrink_to_lo()),
+                        lint_level: lint_level.as_str(),
+                        lint_name: "non_exhaustive_omitted_patterns",
+                    };
+
+                    use rustc_errors::DecorateLint;
+                    let mut err = cx.tcx.sess.struct_span_warn(arm.pat.span(), "");
+                    err.set_primary_message(decorator.msg());
+                    decorator.decorate_lint(&mut err);
+                    err.emit();
+                }
+            }
         }
     }
 
diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs
index 28765af20ad..42b2f18869c 100644
--- a/compiler/rustc_mir_transform/src/check_alignment.rs
+++ b/compiler/rustc_mir_transform/src/check_alignment.rs
@@ -1,13 +1,12 @@
 use crate::MirPass;
-use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItem;
 use rustc_index::IndexVec;
 use rustc_middle::mir::*;
 use rustc_middle::mir::{
     interpret::Scalar,
-    visit::{PlaceContext, Visitor},
+    visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor},
 };
-use rustc_middle::ty::{Ty, TyCtxt, TypeAndMut};
+use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeAndMut};
 use rustc_session::Session;
 
 pub struct CheckAlignment;
@@ -30,7 +29,12 @@ impl<'tcx> MirPass<'tcx> for CheckAlignment {
 
         let basic_blocks = body.basic_blocks.as_mut();
         let local_decls = &mut body.local_decls;
+        let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
 
+        // This pass inserts new blocks. Each insertion changes the Location for all
+        // statements/blocks after. Iterating or visiting the MIR in order would require updating
+        // our current location after every insertion. By iterating backwards, we dodge this issue:
+        // The only Locations that an insertion changes have already been handled.
         for block in (0..basic_blocks.len()).rev() {
             let block = block.into();
             for statement_index in (0..basic_blocks[block].statements.len()).rev() {
@@ -38,22 +42,19 @@ impl<'tcx> MirPass<'tcx> for CheckAlignment {
                 let statement = &basic_blocks[block].statements[statement_index];
                 let source_info = statement.source_info;
 
-                let mut finder = PointerFinder {
-                    local_decls,
-                    tcx,
-                    pointers: Vec::new(),
-                    def_id: body.source.def_id(),
-                };
-                for (pointer, pointee_ty) in finder.find_pointers(statement) {
-                    debug!("Inserting alignment check for {:?}", pointer.ty(&*local_decls, tcx).ty);
+                let mut finder =
+                    PointerFinder { tcx, local_decls, param_env, pointers: Vec::new() };
+                finder.visit_statement(statement, location);
 
+                for (local, ty) in finder.pointers {
+                    debug!("Inserting alignment check for {:?}", ty);
                     let new_block = split_block(basic_blocks, location);
                     insert_alignment_check(
                         tcx,
                         local_decls,
                         &mut basic_blocks[block],
-                        pointer,
-                        pointee_ty,
+                        local,
+                        ty,
                         source_info,
                         new_block,
                     );
@@ -63,69 +64,71 @@ impl<'tcx> MirPass<'tcx> for CheckAlignment {
     }
 }
 
-impl<'tcx, 'a> PointerFinder<'tcx, 'a> {
-    fn find_pointers(&mut self, statement: &Statement<'tcx>) -> Vec<(Place<'tcx>, Ty<'tcx>)> {
-        self.pointers.clear();
-        self.visit_statement(statement, Location::START);
-        core::mem::take(&mut self.pointers)
-    }
-}
-
 struct PointerFinder<'tcx, 'a> {
-    local_decls: &'a mut LocalDecls<'tcx>,
     tcx: TyCtxt<'tcx>,
-    def_id: DefId,
+    local_decls: &'a mut LocalDecls<'tcx>,
+    param_env: ParamEnv<'tcx>,
     pointers: Vec<(Place<'tcx>, Ty<'tcx>)>,
 }
 
 impl<'tcx, 'a> Visitor<'tcx> for PointerFinder<'tcx, 'a> {
-    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
-        if let Rvalue::AddressOf(..) = rvalue {
-            // Ignore dereferences inside of an AddressOf
-            return;
+    fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
+        // We want to only check reads and writes to Places, so we specifically exclude
+        // Borrows and AddressOf.
+        match context {
+            PlaceContext::MutatingUse(
+                MutatingUseContext::Store
+                | MutatingUseContext::AsmOutput
+                | MutatingUseContext::Call
+                | MutatingUseContext::Yield
+                | MutatingUseContext::Drop,
+            ) => {}
+            PlaceContext::NonMutatingUse(
+                NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
+            ) => {}
+            _ => {
+                return;
+            }
         }
-        self.super_rvalue(rvalue, location);
-    }
 
-    fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
-        if let PlaceContext::NonUse(_) = context {
-            return;
-        }
         if !place.is_indirect() {
             return;
         }
 
+        // Since Deref projections must come first and only once, the pointer for an indirect place
+        // is the Local that the Place is based on.
         let pointer = Place::from(place.local);
-        let pointer_ty = pointer.ty(&*self.local_decls, self.tcx).ty;
+        let pointer_ty = self.local_decls[place.local].ty;
 
-        // We only want to check unsafe pointers
+        // We only want to check places based on unsafe pointers
         if !pointer_ty.is_unsafe_ptr() {
-            trace!("Indirect, but not an unsafe ptr, not checking {:?}", pointer_ty);
+            trace!("Indirect, but not based on an unsafe ptr, not checking {:?}", place);
             return;
         }
 
-        let Some(pointee) = pointer_ty.builtin_deref(true) else {
-            debug!("Indirect but no builtin deref: {:?}", pointer_ty);
+        let pointee_ty =
+            pointer_ty.builtin_deref(true).expect("no builtin_deref for an unsafe pointer").ty;
+        // Ideally we'd support this in the future, but for now we are limited to sized types.
+        if !pointee_ty.is_sized(self.tcx, self.param_env) {
+            debug!("Unsafe pointer, but pointee is not known to be sized: {:?}", pointer_ty);
             return;
-        };
-        let mut pointee_ty = pointee.ty;
-        if pointee_ty.is_array() || pointee_ty.is_slice() || pointee_ty.is_str() {
-            pointee_ty = pointee_ty.sequence_element_type(self.tcx);
         }
 
-        if !pointee_ty.is_sized(self.tcx, self.tcx.param_env_reveal_all_normalized(self.def_id)) {
-            debug!("Unsafe pointer, but unsized: {:?}", pointer_ty);
+        // Try to detect types we are sure have an alignment of 1 and skip the check
+        // We don't need to look for str and slices, we already rejected unsized types above
+        let element_ty = match pointee_ty.kind() {
+            ty::Array(ty, _) => *ty,
+            _ => pointee_ty,
+        };
+        if [self.tcx.types.bool, self.tcx.types.i8, self.tcx.types.u8].contains(&element_ty) {
+            debug!("Trivially aligned place type: {:?}", pointee_ty);
             return;
         }
 
-        if [self.tcx.types.bool, self.tcx.types.i8, self.tcx.types.u8, self.tcx.types.str_]
-            .contains(&pointee_ty)
-        {
-            debug!("Trivially aligned pointee type: {:?}", pointer_ty);
-            return;
-        }
+        // Ensure that this place is based on an aligned pointer.
+        self.pointers.push((pointer, pointee_ty));
 
-        self.pointers.push((pointer, pointee_ty))
+        self.super_place(place, context, location);
     }
 }
 
diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs
index 4d0e261ed1f..261d9dd448d 100644
--- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs
+++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs
@@ -7,6 +7,7 @@ use rustc_middle::mir::visit::Visitor;
 use rustc_middle::mir::*;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
+use rustc_session::config::InliningThreshold;
 use rustc_session::config::OptLevel;
 
 pub fn provide(providers: &mut Providers) {
@@ -54,6 +55,12 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
         return false;
     }
 
+    let threshold = match tcx.sess.opts.unstable_opts.cross_crate_inline_threshold {
+        InliningThreshold::Always => return true,
+        InliningThreshold::Sometimes(threshold) => threshold,
+        InliningThreshold::Never => return false,
+    };
+
     let mir = tcx.optimized_mir(def_id);
     let mut checker =
         CostChecker { tcx, callee_body: mir, calls: 0, statements: 0, landing_pads: 0, resumes: 0 };
@@ -61,8 +68,7 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
     checker.calls == 0
         && checker.resumes == 0
         && checker.landing_pads == 0
-        && checker.statements
-            <= tcx.sess.opts.unstable_opts.cross_crate_inline_threshold.unwrap_or(100)
+        && checker.statements <= threshold
 }
 
 struct CostChecker<'b, 'tcx> {
diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs
index 427cc1e1924..7de4ca66794 100644
--- a/compiler/rustc_mir_transform/src/sroa.rs
+++ b/compiler/rustc_mir_transform/src/sroa.rs
@@ -7,7 +7,7 @@ use rustc_middle::mir::visit::*;
 use rustc_middle::mir::*;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_mir_dataflow::value_analysis::{excluded_locals, iter_fields};
-use rustc_target::abi::{FieldIdx, ReprFlags, FIRST_VARIANT};
+use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
 
 pub struct ScalarReplacementOfAggregates;
 
@@ -66,7 +66,7 @@ fn escaping_locals<'tcx>(
             return true;
         }
         if let ty::Adt(def, _args) = ty.kind() {
-            if def.repr().flags.contains(ReprFlags::IS_SIMD) {
+            if def.repr().simd() {
                 // Exclude #[repr(simd)] types so that they are not de-optimized into an array
                 return true;
             }
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index e51b672205c..266190da035 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -270,6 +270,9 @@ parse_fn_ptr_with_generics = function pointer types may not have generic paramet
         *[false] a
     } `for` parameter list
 
+parse_fn_trait_missing_paren = `Fn` bounds require arguments in parentheses
+    .add_paren = add the missing parentheses
+
 parse_forgot_paren = perhaps you forgot parentheses?
 
 parse_found_expr_would_be_stmt = expected expression, found `{$token}`
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 20b4292701e..8ab1ec298a1 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -1378,6 +1378,34 @@ pub(crate) struct FnPtrWithGenericsSugg {
     pub for_param_list_exists: bool,
 }
 
+pub(crate) struct FnTraitMissingParen {
+    pub span: Span,
+    pub machine_applicable: bool,
+}
+
+impl AddToDiagnostic for FnTraitMissingParen {
+    fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+    where
+        F: Fn(
+            &mut rustc_errors::Diagnostic,
+            rustc_errors::SubdiagnosticMessage,
+        ) -> rustc_errors::SubdiagnosticMessage,
+    {
+        diag.span_label(self.span, crate::fluent_generated::parse_fn_trait_missing_paren);
+        let applicability = if self.machine_applicable {
+            Applicability::MachineApplicable
+        } else {
+            Applicability::MaybeIncorrect
+        };
+        diag.span_suggestion_short(
+            self.span.shrink_to_hi(),
+            crate::fluent_generated::parse_add_paren,
+            "()",
+            applicability,
+        );
+    }
+}
+
 #[derive(Diagnostic)]
 #[diag(parse_unexpected_if_with_if)]
 pub(crate) struct UnexpectedIfWithIf(
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 36125e138b2..19690a6964b 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -2303,13 +2303,14 @@ impl<'a> Parser<'a> {
     /// Parses an optional `move` prefix to a closure-like construct.
     fn parse_capture_clause(&mut self) -> PResult<'a, CaptureBy> {
         if self.eat_keyword(kw::Move) {
+            let move_kw_span = self.prev_token.span;
             // Check for `move async` and recover
             if self.check_keyword(kw::Async) {
                 let move_async_span = self.token.span.with_lo(self.prev_token.span.data().lo);
                 Err(errors::AsyncMoveOrderIncorrect { span: move_async_span }
                     .into_diagnostic(&self.sess.span_diagnostic))
             } else {
-                Ok(CaptureBy::Value)
+                Ok(CaptureBy::Value { move_kw: move_kw_span })
             }
         } else {
             Ok(CaptureBy::Ref)
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 253dd2a3b34..65d41ea19fd 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -2278,6 +2278,18 @@ impl<'a> Parser<'a> {
                     err.span_label(ident.span, "while parsing this `fn`");
                     err.emit();
                 } else {
+                    // check for typo'd Fn* trait bounds such as
+                    // fn foo<F>() where F: FnOnce -> () {}
+                    if self.token.kind == token::RArrow {
+                        let machine_applicable = [sym::FnOnce, sym::FnMut, sym::Fn]
+                            .into_iter()
+                            .any(|s| self.prev_token.is_ident_named(s));
+
+                        err.subdiagnostic(errors::FnTraitMissingParen {
+                            span: self.prev_token.span,
+                            machine_applicable,
+                        });
+                    }
                     return Err(err);
                 }
             }
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index edcc22d56c6..93db6cfc463 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -1545,7 +1545,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         let (span, sugg, post) = if let SuggestionTarget::SimilarlyNamed = suggestion.target
             && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
             && let Some(span) = suggestion.span
-            && let Some(candidate) = suggestion.candidate.as_str().strip_prefix("_")
+            && let Some(candidate) = suggestion.candidate.as_str().strip_prefix('_')
             && snippet == candidate
         {
             // When the suggested binding change would be from `x` to `_x`, suggest changing the
diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs
index f792b8f2cdd..e1eb58fecc7 100644
--- a/compiler/rustc_session/src/code_stats.rs
+++ b/compiler/rustc_session/src/code_stats.rs
@@ -226,7 +226,7 @@ impl CodeStats {
         }
     }
 
-    pub fn print_vtable_sizes(&self, crate_name: &str) {
+    pub fn print_vtable_sizes(&self, crate_name: Symbol) {
         let mut infos =
             std::mem::take(&mut *self.vtable_sizes.lock()).into_values().collect::<Vec<_>>();
 
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index c94e0d0ed39..f745bc390ca 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -2477,7 +2477,7 @@ pub fn parse_externs(
             let mut error = handler.early_struct_error(format!(
                 "crate name `{name}` passed to `--extern` is not a valid ASCII identifier"
             ));
-            let adjusted_name = name.replace("-", "_");
+            let adjusted_name = name.replace('-', "_");
             if crate::utils::is_ascii_ident(&adjusted_name) {
                 error.help(format!(
                     "consider replacing the dashes with underscores: `{adjusted_name}`"
@@ -3161,10 +3161,10 @@ impl PpMode {
 pub(crate) mod dep_tracking {
     use super::{
         BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, DebugInfoCompression,
-        ErrorOutputType, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail,
-        LtoCli, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes, Polonius,
-        RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind,
-        SwitchWithOptPath, SymbolManglingVersion, TraitSolver, TrimmedDefPaths,
+        ErrorOutputType, InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto,
+        LocationDetail, LtoCli, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes,
+        Polonius, RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm,
+        SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, TraitSolver, TrimmedDefPaths,
     };
     use crate::lint;
     use crate::options::WasiExecModel;
@@ -3270,6 +3270,7 @@ pub(crate) mod dep_tracking {
         LanguageIdentifier,
         TraitSolver,
         Polonius,
+        InliningThreshold,
     );
 
     impl<T1, T2> DepTrackingHash for (T1, T2)
@@ -3435,3 +3436,16 @@ impl Polonius {
         matches!(self, Polonius::Next)
     }
 }
+
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum InliningThreshold {
+    Always,
+    Sometimes(usize),
+    Never,
+}
+
+impl Default for InliningThreshold {
+    fn default() -> Self {
+        Self::Sometimes(100)
+    }
+}
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 7510a41485a..964a26e94fe 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -428,6 +428,8 @@ mod desc {
         "one of supported execution strategies (`same-thread`, or `cross-thread`)";
     pub const parse_dump_solver_proof_tree: &str = "one of: `always`, `on-request`, `on-error`";
     pub const parse_remap_path_scope: &str = "comma separated list of scopes: `macro`, `diagnostics`, `unsplit-debuginfo`, `split-debuginfo`, `split-debuginfo-path`, `object`, `all`";
+    pub const parse_inlining_threshold: &str =
+        "either a boolean (`yes`, `no`, `on`, `off`, etc), or a non-negative number";
 }
 
 mod parse {
@@ -1310,6 +1312,26 @@ mod parse {
         };
         true
     }
+
+    pub(crate) fn parse_inlining_threshold(slot: &mut InliningThreshold, v: Option<&str>) -> bool {
+        match v {
+            Some("always" | "yes") => {
+                *slot = InliningThreshold::Always;
+            }
+            Some("never") => {
+                *slot = InliningThreshold::Never;
+            }
+            Some(v) => {
+                if let Ok(threshold) = v.parse() {
+                    *slot = InliningThreshold::Sometimes(threshold);
+                } else {
+                    return false;
+                }
+            }
+            None => return false,
+        }
+        true
+    }
 }
 
 options! {
@@ -1479,7 +1501,7 @@ options! {
         "combine CGUs into a single one"),
     crate_attr: Vec<String> = (Vec::new(), parse_string_push, [TRACKED],
         "inject the given attribute in the crate"),
-    cross_crate_inline_threshold: Option<usize> = (None, parse_opt_number, [TRACKED],
+    cross_crate_inline_threshold: InliningThreshold = (InliningThreshold::Sometimes(100), parse_inlining_threshold, [TRACKED],
         "threshold to allow cross crate inlining of functions"),
     debug_info_for_profiling: bool = (false, parse_bool, [TRACKED],
         "emit discriminators and other data necessary for AutoFDO"),
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index ff470ce1fa0..f287862cc23 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -194,6 +194,9 @@ symbols! {
         Error,
         File,
         FileType,
+        Fn,
+        FnMut,
+        FnOnce,
         FormatSpec,
         Formatter,
         From,
@@ -1114,6 +1117,7 @@ symbols! {
         off,
         offset,
         offset_of,
+        offset_of_enum,
         omit_gdb_pretty_printer_section,
         on,
         on_unimplemented,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 31da437f2e9..6b09bc89873 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -28,6 +28,7 @@ use rustc_infer::infer::error_reporting::TypeErrCtxt;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::infer::{DefineOpaqueTypes, InferOk, LateBoundRegionConversionTime};
 use rustc_middle::hir::map;
+use rustc_middle::traits::IsConstable;
 use rustc_middle::ty::error::TypeError::{self, Sorts};
 use rustc_middle::ty::{
     self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, GenericArgs,
@@ -2768,20 +2769,30 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     ));
                 }
             }
-            ObligationCauseCode::RepeatElementCopy { is_const_fn } => {
+            ObligationCauseCode::RepeatElementCopy { is_constable, elt_type, elt_span, elt_stmt_span } => {
                 err.note(
                     "the `Copy` trait is required because this value will be copied for each element of the array",
                 );
-
-                if is_const_fn {
-                    err.help(
-                        "consider creating a new `const` item and initializing it with the result \
-                        of the function call to be used in the repeat position, like \
-                        `const VAL: Type = const_fn();` and `let x = [VAL; 42];`",
-                    );
+                let value_kind = match is_constable {
+                    IsConstable::Fn => Some("the result of the function call"),
+                    IsConstable::Ctor => Some("the result of the constructor"),
+                    _ => None
+                };
+                let sm = tcx.sess.source_map();
+                if let Some(value_kind) = value_kind &&
+                    let Ok(snip) = sm.span_to_snippet(elt_span)
+                {
+                    let help_msg = format!(
+                        "consider creating a new `const` item and initializing it with {value_kind} \
+                        to be used in the repeat position");
+                    let indentation = sm.indentation_before(elt_stmt_span).unwrap_or_default();
+                    err.multipart_suggestion(help_msg, vec![
+                        (elt_stmt_span.shrink_to_lo(), format!("const ARRAY_REPEAT_VALUE: {elt_type} = {snip};\n{indentation}")),
+                        (elt_span, "ARRAY_REPEAT_VALUE".to_string())
+                    ], Applicability::MachineApplicable);
                 }
 
-                if self.tcx.sess.is_nightly_build() && is_const_fn {
+                if self.tcx.sess.is_nightly_build() && matches!(is_constable, IsConstable::Fn|IsConstable::Ctor) {
                     err.help(
                         "create an inline `const` block, see RFC #2920 \
                          <https://github.com/rust-lang/rfcs/pull/2920> for more information",
@@ -2938,7 +2949,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 else {
                     bug!("expected closure in SizedClosureCapture obligation");
                 };
-                if let hir::CaptureBy::Value = closure.capture_clause
+                if let hir::CaptureBy::Value { .. } = closure.capture_clause
                     && let Some(span) = closure.fn_arg_span
                 {
                     err.span_label(span, "this closure captures all values by move");
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index e4f7592c409..471d10dbdbd 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -783,7 +783,7 @@ pub struct BoundVarReplacer<'me, 'tcx> {
     // the `var` (but we *could* bring that into scope if we were to track them as we pass them).
     mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
     mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
-    mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>,
+    mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
     // The current depth relative to *this* folding, *not* the entire normalization. In other words,
     // the depth of binders we've passed here.
     current_index: ty::DebruijnIndex,
@@ -843,11 +843,11 @@ impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> {
         T,
         BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
         BTreeMap<ty::PlaceholderType, ty::BoundTy>,
-        BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>,
+        BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
     ) {
         let mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion> = BTreeMap::new();
         let mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy> = BTreeMap::new();
-        let mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar> = BTreeMap::new();
+        let mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar> = BTreeMap::new();
 
         let mut replacer = BoundVarReplacer {
             infcx,
@@ -966,7 +966,7 @@ pub struct PlaceholderReplacer<'me, 'tcx> {
     infcx: &'me InferCtxt<'tcx>,
     mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
     mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
-    mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>,
+    mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
     universe_indices: &'me [Option<ty::UniverseIndex>],
     current_index: ty::DebruijnIndex,
 }
@@ -976,7 +976,7 @@ impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> {
         infcx: &'me InferCtxt<'tcx>,
         mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
         mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
-        mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>,
+        mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
         universe_indices: &'me [Option<ty::UniverseIndex>],
         value: T,
     ) -> T {
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index 5c67188dd24..f4b6d3bcfda 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -628,15 +628,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
 
         self.infcx.probe(|_snapshot| {
-            if obligation.has_non_region_late_bound() {
-                return;
-            }
+            let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate);
+            let placeholder_trait_predicate =
+                self.infcx.instantiate_binder_with_placeholders(poly_trait_predicate);
 
-            // The code below doesn't care about regions, and the
-            // self-ty here doesn't escape this probe, so just erase
-            // any LBR.
-            let self_ty = self.tcx().erase_late_bound_regions(obligation.self_ty());
-            let poly_trait_ref = match self_ty.kind() {
+            let self_ty = placeholder_trait_predicate.self_ty();
+            let principal_trait_ref = match self_ty.kind() {
                 ty::Dynamic(ref data, ..) => {
                     if data.auto_traits().any(|did| did == obligation.predicate.def_id()) {
                         debug!(
@@ -668,18 +665,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 _ => return,
             };
 
-            debug!(?poly_trait_ref, "assemble_candidates_from_object_ty");
-
-            let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate);
-            let placeholder_trait_predicate =
-                self.infcx.instantiate_binder_with_placeholders(poly_trait_predicate);
+            debug!(?principal_trait_ref, "assemble_candidates_from_object_ty");
 
             // Count only those upcast versions that match the trait-ref
             // we are looking for. Specifically, do not only check for the
             // correct trait, but also the correct type parameters.
             // For example, we may be trying to upcast `Foo` to `Bar<i32>`,
             // but `Foo` is declared as `trait Foo: Bar<u32>`.
-            let candidate_supertraits = util::supertraits(self.tcx(), poly_trait_ref)
+            let candidate_supertraits = util::supertraits(self.tcx(), principal_trait_ref)
                 .enumerate()
                 .filter(|&(_, upcast_trait_ref)| {
                     self.infcx.probe(|_| {
diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs
index c8e730b585a..ace9eade7f6 100644
--- a/compiler/rustc_type_ir/src/canonical.rs
+++ b/compiler/rustc_type_ir/src/canonical.rs
@@ -3,18 +3,17 @@ use std::hash::Hash;
 use std::ops::ControlFlow;
 
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_serialize::{Decodable, Encodable};
 
 use crate::fold::{FallibleTypeFolder, TypeFoldable};
 use crate::visit::{TypeVisitable, TypeVisitor};
-use crate::TyDecoder;
-use crate::{HashStableContext, Interner, TyEncoder, UniverseIndex};
+use crate::{HashStableContext, Interner, UniverseIndex};
 
 /// A "canonicalized" type `V` is one where all free inference
 /// variables have been rewritten to "canonical vars". These are
 /// numbered starting from 0 in order of first appearance.
 #[derive(derivative::Derivative)]
 #[derivative(Clone(bound = "V: Clone"), Hash(bound = "V: Hash"))]
+#[derive(TyEncodable, TyDecodable)]
 pub struct Canonical<I: Interner, V> {
     pub value: V,
     pub max_universe: UniverseIndex,
@@ -127,27 +126,3 @@ where
         self.variables.visit_with(folder)
     }
 }
-
-impl<I: Interner, E: TyEncoder<I = I>, V: Encodable<E>> Encodable<E> for Canonical<I, V>
-where
-    I::CanonicalVars: Encodable<E>,
-{
-    fn encode(&self, s: &mut E) {
-        self.value.encode(s);
-        self.max_universe.encode(s);
-        self.variables.encode(s);
-    }
-}
-
-impl<I: Interner, D: TyDecoder<I = I>, V: Decodable<D>> Decodable<D> for Canonical<I, V>
-where
-    I::CanonicalVars: Decodable<D>,
-{
-    fn decode(d: &mut D) -> Self {
-        Canonical {
-            value: Decodable::decode(d),
-            max_universe: Decodable::decode(d),
-            variables: Decodable::decode(d),
-        }
-    }
-}
diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs
index cf67ba0b21a..33782b13ca8 100644
--- a/compiler/rustc_type_ir/src/const_kind.rs
+++ b/compiler/rustc_type_ir/src/const_kind.rs
@@ -1,12 +1,8 @@
 use rustc_data_structures::stable_hasher::HashStable;
 use rustc_data_structures::stable_hasher::StableHasher;
-use rustc_serialize::{Decodable, Decoder, Encodable};
 use std::fmt;
 
-use crate::{
-    DebruijnIndex, DebugWithInfcx, HashStableContext, InferCtxtLike, Interner, TyDecoder,
-    TyEncoder, WithInfcx,
-};
+use crate::{DebruijnIndex, DebugWithInfcx, HashStableContext, InferCtxtLike, Interner, WithInfcx};
 
 use self::ConstKind::*;
 
@@ -20,6 +16,7 @@ use self::ConstKind::*;
     Ord = "feature_allow_slow_enum",
     Hash(bound = "")
 )]
+#[derive(TyEncodable, TyDecodable)]
 pub enum ConstKind<I: Interner> {
     /// A const generic parameter.
     Param(I::ParamConst),
@@ -92,67 +89,6 @@ where
     }
 }
 
-impl<I: Interner, D: TyDecoder<I = I>> Decodable<D> for ConstKind<I>
-where
-    I::ParamConst: Decodable<D>,
-    I::InferConst: Decodable<D>,
-    I::BoundConst: Decodable<D>,
-    I::PlaceholderConst: Decodable<D>,
-    I::AliasConst: Decodable<D>,
-    I::ValueConst: Decodable<D>,
-    I::ErrorGuaranteed: Decodable<D>,
-    I::ExprConst: Decodable<D>,
-{
-    fn decode(d: &mut D) -> Self {
-        match Decoder::read_usize(d) {
-            0 => Param(Decodable::decode(d)),
-            1 => Infer(Decodable::decode(d)),
-            2 => Bound(Decodable::decode(d), Decodable::decode(d)),
-            3 => Placeholder(Decodable::decode(d)),
-            4 => Unevaluated(Decodable::decode(d)),
-            5 => Value(Decodable::decode(d)),
-            6 => Error(Decodable::decode(d)),
-            7 => Expr(Decodable::decode(d)),
-            _ => panic!(
-                "{}",
-                format!(
-                    "invalid enum variant tag while decoding `{}`, expected 0..{}",
-                    "ConstKind", 8,
-                )
-            ),
-        }
-    }
-}
-
-impl<I: Interner, E: TyEncoder<I = I>> Encodable<E> for ConstKind<I>
-where
-    I::ParamConst: Encodable<E>,
-    I::InferConst: Encodable<E>,
-    I::BoundConst: Encodable<E>,
-    I::PlaceholderConst: Encodable<E>,
-    I::AliasConst: Encodable<E>,
-    I::ValueConst: Encodable<E>,
-    I::ErrorGuaranteed: Encodable<E>,
-    I::ExprConst: Encodable<E>,
-{
-    fn encode(&self, e: &mut E) {
-        let disc = const_kind_discriminant(self);
-        match self {
-            Param(p) => e.emit_enum_variant(disc, |e| p.encode(e)),
-            Infer(i) => e.emit_enum_variant(disc, |e| i.encode(e)),
-            Bound(d, b) => e.emit_enum_variant(disc, |e| {
-                d.encode(e);
-                b.encode(e);
-            }),
-            Placeholder(p) => e.emit_enum_variant(disc, |e| p.encode(e)),
-            Unevaluated(u) => e.emit_enum_variant(disc, |e| u.encode(e)),
-            Value(v) => e.emit_enum_variant(disc, |e| v.encode(e)),
-            Error(er) => e.emit_enum_variant(disc, |e| er.encode(e)),
-            Expr(ex) => e.emit_enum_variant(disc, |e| ex.encode(e)),
-        }
-    }
-}
-
 impl<I: Interner> PartialEq for ConstKind<I> {
     fn eq(&self, other: &Self) -> bool {
         match (self, other) {
diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs
index d5cadd4e83a..8472a084505 100644
--- a/compiler/rustc_type_ir/src/flags.rs
+++ b/compiler/rustc_type_ir/src/flags.rs
@@ -115,5 +115,8 @@ bitflags! {
 
         /// Does this have `Coroutine` or `CoroutineWitness`?
         const HAS_TY_COROUTINE            = 1 << 23;
+
+        /// Does this have any binders with bound vars (e.g. that need to be anonymized)?
+        const HAS_BINDER_VARS             = 1 << 24;
     }
 }
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index a056fbeda98..e8785fff2ef 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -10,6 +10,8 @@
 #![deny(rustc::diagnostic_outside_of_impl)]
 #![allow(internal_features)]
 
+extern crate self as rustc_type_ir;
+
 #[macro_use]
 extern crate bitflags;
 #[macro_use]
diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs
index 23117fdd531..48662d42642 100644
--- a/compiler/rustc_type_ir/src/predicate_kind.rs
+++ b/compiler/rustc_type_ir/src/predicate_kind.rs
@@ -1,18 +1,16 @@
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_serialize::Decoder;
-use rustc_serialize::{Decodable, Encodable};
 use std::fmt;
 use std::ops::ControlFlow;
 
 use crate::fold::{FallibleTypeFolder, TypeFoldable};
 use crate::visit::{TypeVisitable, TypeVisitor};
 use crate::{HashStableContext, Interner};
-use crate::{TyDecoder, TyEncoder};
 
 /// A clause is something that can appear in where bounds or be inferred
 /// by implied bounds.
 #[derive(derivative::Derivative)]
 #[derivative(Clone(bound = ""), Hash(bound = ""))]
+#[derive(TyEncodable, TyDecodable)]
 pub enum ClauseKind<I: Interner> {
     /// Corresponds to `where Foo: Bar<A, B, C>`. `Foo` here would be
     /// the `Self` type of the trait reference and `A`, `B`, and `C`
@@ -161,65 +159,9 @@ where
     }
 }
 
-impl<I: Interner, D: TyDecoder<I = I>> Decodable<D> for ClauseKind<I>
-where
-    I::Ty: Decodable<D>,
-    I::Const: Decodable<D>,
-    I::GenericArg: Decodable<D>,
-    I::TraitPredicate: Decodable<D>,
-    I::ProjectionPredicate: Decodable<D>,
-    I::TypeOutlivesPredicate: Decodable<D>,
-    I::RegionOutlivesPredicate: Decodable<D>,
-{
-    fn decode(d: &mut D) -> Self {
-        match Decoder::read_usize(d) {
-            0 => ClauseKind::Trait(Decodable::decode(d)),
-            1 => ClauseKind::RegionOutlives(Decodable::decode(d)),
-            2 => ClauseKind::TypeOutlives(Decodable::decode(d)),
-            3 => ClauseKind::Projection(Decodable::decode(d)),
-            4 => ClauseKind::ConstArgHasType(Decodable::decode(d), Decodable::decode(d)),
-            5 => ClauseKind::WellFormed(Decodable::decode(d)),
-            6 => ClauseKind::ConstEvaluatable(Decodable::decode(d)),
-            _ => panic!(
-                "{}",
-                format!(
-                    "invalid enum variant tag while decoding `{}`, expected 0..{}",
-                    "ClauseKind", 7,
-                )
-            ),
-        }
-    }
-}
-
-impl<I: Interner, E: TyEncoder> Encodable<E> for ClauseKind<I>
-where
-    I::Ty: Encodable<E>,
-    I::Const: Encodable<E>,
-    I::GenericArg: Encodable<E>,
-    I::TraitPredicate: Encodable<E>,
-    I::ProjectionPredicate: Encodable<E>,
-    I::TypeOutlivesPredicate: Encodable<E>,
-    I::RegionOutlivesPredicate: Encodable<E>,
-{
-    fn encode(&self, s: &mut E) {
-        let discriminant = clause_kind_discriminant(self);
-        match self {
-            ClauseKind::Trait(p) => s.emit_enum_variant(discriminant, |s| p.encode(s)),
-            ClauseKind::RegionOutlives(p) => s.emit_enum_variant(discriminant, |s| p.encode(s)),
-            ClauseKind::TypeOutlives(p) => s.emit_enum_variant(discriminant, |s| p.encode(s)),
-            ClauseKind::Projection(p) => s.emit_enum_variant(discriminant, |s| p.encode(s)),
-            ClauseKind::ConstArgHasType(c, t) => s.emit_enum_variant(discriminant, |s| {
-                c.encode(s);
-                t.encode(s);
-            }),
-            ClauseKind::WellFormed(p) => s.emit_enum_variant(discriminant, |s| p.encode(s)),
-            ClauseKind::ConstEvaluatable(p) => s.emit_enum_variant(discriminant, |s| p.encode(s)),
-        }
-    }
-}
-
 #[derive(derivative::Derivative)]
 #[derivative(Clone(bound = ""), Hash(bound = ""))]
+#[derive(TyEncodable, TyDecodable)]
 pub enum PredicateKind<I: Interner> {
     /// Prove a clause
     Clause(ClauseKind<I>),
@@ -418,83 +360,6 @@ where
     }
 }
 
-impl<I: Interner, D: TyDecoder<I = I>> Decodable<D> for PredicateKind<I>
-where
-    I::DefId: Decodable<D>,
-    I::Const: Decodable<D>,
-    I::GenericArgs: Decodable<D>,
-    I::Term: Decodable<D>,
-    I::CoercePredicate: Decodable<D>,
-    I::SubtypePredicate: Decodable<D>,
-    I::ClosureKind: Decodable<D>,
-    ClauseKind<I>: Decodable<D>,
-{
-    fn decode(d: &mut D) -> Self {
-        match Decoder::read_usize(d) {
-            0 => PredicateKind::Clause(Decodable::decode(d)),
-            1 => PredicateKind::ObjectSafe(Decodable::decode(d)),
-            2 => PredicateKind::ClosureKind(
-                Decodable::decode(d),
-                Decodable::decode(d),
-                Decodable::decode(d),
-            ),
-            3 => PredicateKind::Subtype(Decodable::decode(d)),
-            4 => PredicateKind::Coerce(Decodable::decode(d)),
-            5 => PredicateKind::ConstEquate(Decodable::decode(d), Decodable::decode(d)),
-            6 => PredicateKind::Ambiguous,
-            7 => PredicateKind::AliasRelate(
-                Decodable::decode(d),
-                Decodable::decode(d),
-                Decodable::decode(d),
-            ),
-            _ => panic!(
-                "{}",
-                format!(
-                    "invalid enum variant tag while decoding `{}`, expected 0..{}",
-                    "PredicateKind", 8,
-                )
-            ),
-        }
-    }
-}
-
-impl<I: Interner, E: TyEncoder> Encodable<E> for PredicateKind<I>
-where
-    I::DefId: Encodable<E>,
-    I::Const: Encodable<E>,
-    I::GenericArgs: Encodable<E>,
-    I::Term: Encodable<E>,
-    I::CoercePredicate: Encodable<E>,
-    I::SubtypePredicate: Encodable<E>,
-    I::ClosureKind: Encodable<E>,
-    ClauseKind<I>: Encodable<E>,
-{
-    fn encode(&self, s: &mut E) {
-        let discriminant = predicate_kind_discriminant(self);
-        match self {
-            PredicateKind::Clause(c) => s.emit_enum_variant(discriminant, |s| c.encode(s)),
-            PredicateKind::ObjectSafe(d) => s.emit_enum_variant(discriminant, |s| d.encode(s)),
-            PredicateKind::ClosureKind(d, g, k) => s.emit_enum_variant(discriminant, |s| {
-                d.encode(s);
-                g.encode(s);
-                k.encode(s);
-            }),
-            PredicateKind::Subtype(c) => s.emit_enum_variant(discriminant, |s| c.encode(s)),
-            PredicateKind::Coerce(c) => s.emit_enum_variant(discriminant, |s| c.encode(s)),
-            PredicateKind::ConstEquate(a, b) => s.emit_enum_variant(discriminant, |s| {
-                a.encode(s);
-                b.encode(s);
-            }),
-            PredicateKind::Ambiguous => s.emit_enum_variant(discriminant, |_s| {}),
-            PredicateKind::AliasRelate(a, b, d) => s.emit_enum_variant(discriminant, |s| {
-                a.encode(s);
-                b.encode(s);
-                d.encode(s);
-            }),
-        }
-    }
-}
-
 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)]
 #[derive(HashStable_Generic, Encodable, Decodable)]
 pub enum AliasRelationDirection {
diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs
index 72f86fc0692..69ed5badaea 100644
--- a/compiler/rustc_type_ir/src/region_kind.rs
+++ b/compiler/rustc_type_ir/src/region_kind.rs
@@ -1,12 +1,8 @@
 use rustc_data_structures::stable_hasher::HashStable;
 use rustc_data_structures::stable_hasher::StableHasher;
-use rustc_serialize::{Decodable, Decoder, Encodable};
 use std::fmt;
 
-use crate::{
-    DebruijnIndex, DebugWithInfcx, HashStableContext, InferCtxtLike, Interner, TyDecoder,
-    TyEncoder, WithInfcx,
-};
+use crate::{DebruijnIndex, DebugWithInfcx, HashStableContext, InferCtxtLike, Interner, WithInfcx};
 
 use self::RegionKind::*;
 
@@ -125,6 +121,7 @@ use self::RegionKind::*;
     Ord = "feature_allow_slow_enum",
     Hash(bound = "")
 )]
+#[derive(TyEncodable, TyDecodable)]
 pub enum RegionKind<I: Interner> {
     /// Region bound in a type or fn declaration which will be
     /// substituted 'early' -- that is, at the same time when type
@@ -245,72 +242,6 @@ impl<I: Interner> fmt::Debug for RegionKind<I> {
     }
 }
 
-// This is manually implemented because a derive would require `I: Encodable`
-impl<I: Interner, E: TyEncoder<I = I>> Encodable<E> for RegionKind<I>
-where
-    I::EarlyBoundRegion: Encodable<E>,
-    I::BoundRegion: Encodable<E>,
-    I::FreeRegion: Encodable<E>,
-    I::InferRegion: Encodable<E>,
-    I::PlaceholderRegion: Encodable<E>,
-{
-    fn encode(&self, e: &mut E) {
-        let disc = regionkind_discriminant(self);
-        match self {
-            ReEarlyBound(a) => e.emit_enum_variant(disc, |e| {
-                a.encode(e);
-            }),
-            ReLateBound(a, b) => e.emit_enum_variant(disc, |e| {
-                a.encode(e);
-                b.encode(e);
-            }),
-            ReFree(a) => e.emit_enum_variant(disc, |e| {
-                a.encode(e);
-            }),
-            ReStatic => e.emit_enum_variant(disc, |_| {}),
-            ReVar(a) => e.emit_enum_variant(disc, |e| {
-                a.encode(e);
-            }),
-            RePlaceholder(a) => e.emit_enum_variant(disc, |e| {
-                a.encode(e);
-            }),
-            ReErased => e.emit_enum_variant(disc, |_| {}),
-            ReError(_) => e.emit_enum_variant(disc, |_| {}),
-        }
-    }
-}
-
-// This is manually implemented because a derive would require `I: Decodable`
-impl<I: Interner, D: TyDecoder<I = I>> Decodable<D> for RegionKind<I>
-where
-    I::EarlyBoundRegion: Decodable<D>,
-    I::BoundRegion: Decodable<D>,
-    I::FreeRegion: Decodable<D>,
-    I::InferRegion: Decodable<D>,
-    I::PlaceholderRegion: Decodable<D>,
-    I::ErrorGuaranteed: Decodable<D>,
-{
-    fn decode(d: &mut D) -> Self {
-        match Decoder::read_usize(d) {
-            0 => ReEarlyBound(Decodable::decode(d)),
-            1 => ReLateBound(Decodable::decode(d), Decodable::decode(d)),
-            2 => ReFree(Decodable::decode(d)),
-            3 => ReStatic,
-            4 => ReVar(Decodable::decode(d)),
-            5 => RePlaceholder(Decodable::decode(d)),
-            6 => ReErased,
-            7 => ReError(Decodable::decode(d)),
-            _ => panic!(
-                "{}",
-                format!(
-                    "invalid enum variant tag while decoding `{}`, expected 0..{}",
-                    "RegionKind", 8,
-                )
-            ),
-        }
-    }
-}
-
 // This is not a derived impl because a derive would require `I: HashStable`
 impl<CTX: HashStableContext, I: Interner> HashStable<CTX> for RegionKind<I>
 where
diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs
index 2138c273341..09a9a332269 100644
--- a/compiler/rustc_type_ir/src/ty_kind.rs
+++ b/compiler/rustc_type_ir/src/ty_kind.rs
@@ -2,14 +2,11 @@
 
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::unify::{EqUnifyValue, UnifyKey};
-use rustc_serialize::{Decodable, Decoder, Encodable};
 use std::fmt;
 use std::mem::discriminant;
 
 use crate::HashStableContext;
 use crate::Interner;
-use crate::TyDecoder;
-use crate::TyEncoder;
 use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, WithInfcx};
 
 use self::TyKind::*;
@@ -122,6 +119,7 @@ pub enum AliasKind {
     Ord = "feature_allow_slow_enum",
     Hash(bound = "")
 )]
+#[derive(TyEncodable, TyDecodable)]
 pub enum TyKind<I: Interner> {
     /// The primitive boolean type. Written as `bool`.
     Bool,
@@ -472,178 +470,6 @@ impl<I: Interner> fmt::Debug for TyKind<I> {
     }
 }
 
-// This is manually implemented because a derive would require `I: Encodable`
-impl<I: Interner, E: TyEncoder<I = I>> Encodable<E> for TyKind<I>
-where
-    I::ErrorGuaranteed: Encodable<E>,
-    I::AdtDef: Encodable<E>,
-    I::GenericArgs: Encodable<E>,
-    I::DefId: Encodable<E>,
-    I::Ty: Encodable<E>,
-    I::Const: Encodable<E>,
-    I::Region: Encodable<E>,
-    I::TypeAndMut: Encodable<E>,
-    I::PolyFnSig: Encodable<E>,
-    I::BoundExistentialPredicates: Encodable<E>,
-    I::Tys: Encodable<E>,
-    I::AliasTy: Encodable<E>,
-    I::ParamTy: Encodable<E>,
-    I::BoundTy: Encodable<E>,
-    I::PlaceholderTy: Encodable<E>,
-    I::InferTy: Encodable<E>,
-    I::AllocId: Encodable<E>,
-{
-    fn encode(&self, e: &mut E) {
-        let disc = tykind_discriminant(self);
-        match self {
-            Bool => e.emit_enum_variant(disc, |_| {}),
-            Char => e.emit_enum_variant(disc, |_| {}),
-            Int(i) => e.emit_enum_variant(disc, |e| {
-                i.encode(e);
-            }),
-            Uint(u) => e.emit_enum_variant(disc, |e| {
-                u.encode(e);
-            }),
-            Float(f) => e.emit_enum_variant(disc, |e| {
-                f.encode(e);
-            }),
-            Adt(adt, args) => e.emit_enum_variant(disc, |e| {
-                adt.encode(e);
-                args.encode(e);
-            }),
-            Foreign(def_id) => e.emit_enum_variant(disc, |e| {
-                def_id.encode(e);
-            }),
-            Str => e.emit_enum_variant(disc, |_| {}),
-            Array(t, c) => e.emit_enum_variant(disc, |e| {
-                t.encode(e);
-                c.encode(e);
-            }),
-            Slice(t) => e.emit_enum_variant(disc, |e| {
-                t.encode(e);
-            }),
-            RawPtr(tam) => e.emit_enum_variant(disc, |e| {
-                tam.encode(e);
-            }),
-            Ref(r, t, m) => e.emit_enum_variant(disc, |e| {
-                r.encode(e);
-                t.encode(e);
-                m.encode(e);
-            }),
-            FnDef(def_id, args) => e.emit_enum_variant(disc, |e| {
-                def_id.encode(e);
-                args.encode(e);
-            }),
-            FnPtr(polyfnsig) => e.emit_enum_variant(disc, |e| {
-                polyfnsig.encode(e);
-            }),
-            Dynamic(l, r, repr) => e.emit_enum_variant(disc, |e| {
-                l.encode(e);
-                r.encode(e);
-                repr.encode(e);
-            }),
-            Closure(def_id, args) => e.emit_enum_variant(disc, |e| {
-                def_id.encode(e);
-                args.encode(e);
-            }),
-            Coroutine(def_id, args, m) => e.emit_enum_variant(disc, |e| {
-                def_id.encode(e);
-                args.encode(e);
-                m.encode(e);
-            }),
-            CoroutineWitness(def_id, args) => e.emit_enum_variant(disc, |e| {
-                def_id.encode(e);
-                args.encode(e);
-            }),
-            Never => e.emit_enum_variant(disc, |_| {}),
-            Tuple(args) => e.emit_enum_variant(disc, |e| {
-                args.encode(e);
-            }),
-            Alias(k, p) => e.emit_enum_variant(disc, |e| {
-                k.encode(e);
-                p.encode(e);
-            }),
-            Param(p) => e.emit_enum_variant(disc, |e| {
-                p.encode(e);
-            }),
-            Bound(d, b) => e.emit_enum_variant(disc, |e| {
-                d.encode(e);
-                b.encode(e);
-            }),
-            Placeholder(p) => e.emit_enum_variant(disc, |e| {
-                p.encode(e);
-            }),
-            Infer(i) => e.emit_enum_variant(disc, |e| {
-                i.encode(e);
-            }),
-            Error(d) => e.emit_enum_variant(disc, |e| {
-                d.encode(e);
-            }),
-        }
-    }
-}
-
-// This is manually implemented because a derive would require `I: Decodable`
-impl<I: Interner, D: TyDecoder<I = I>> Decodable<D> for TyKind<I>
-where
-    I::ErrorGuaranteed: Decodable<D>,
-    I::AdtDef: Decodable<D>,
-    I::GenericArgs: Decodable<D>,
-    I::DefId: Decodable<D>,
-    I::Ty: Decodable<D>,
-    I::Const: Decodable<D>,
-    I::Region: Decodable<D>,
-    I::TypeAndMut: Decodable<D>,
-    I::PolyFnSig: Decodable<D>,
-    I::BoundExistentialPredicates: Decodable<D>,
-    I::Tys: Decodable<D>,
-    I::AliasTy: Decodable<D>,
-    I::ParamTy: Decodable<D>,
-    I::AliasTy: Decodable<D>,
-    I::BoundTy: Decodable<D>,
-    I::PlaceholderTy: Decodable<D>,
-    I::InferTy: Decodable<D>,
-    I::AllocId: Decodable<D>,
-{
-    fn decode(d: &mut D) -> Self {
-        match Decoder::read_usize(d) {
-            0 => Bool,
-            1 => Char,
-            2 => Int(Decodable::decode(d)),
-            3 => Uint(Decodable::decode(d)),
-            4 => Float(Decodable::decode(d)),
-            5 => Adt(Decodable::decode(d), Decodable::decode(d)),
-            6 => Foreign(Decodable::decode(d)),
-            7 => Str,
-            8 => Array(Decodable::decode(d), Decodable::decode(d)),
-            9 => Slice(Decodable::decode(d)),
-            10 => RawPtr(Decodable::decode(d)),
-            11 => Ref(Decodable::decode(d), Decodable::decode(d), Decodable::decode(d)),
-            12 => FnDef(Decodable::decode(d), Decodable::decode(d)),
-            13 => FnPtr(Decodable::decode(d)),
-            14 => Dynamic(Decodable::decode(d), Decodable::decode(d), Decodable::decode(d)),
-            15 => Closure(Decodable::decode(d), Decodable::decode(d)),
-            16 => Coroutine(Decodable::decode(d), Decodable::decode(d), Decodable::decode(d)),
-            17 => CoroutineWitness(Decodable::decode(d), Decodable::decode(d)),
-            18 => Never,
-            19 => Tuple(Decodable::decode(d)),
-            20 => Alias(Decodable::decode(d), Decodable::decode(d)),
-            21 => Param(Decodable::decode(d)),
-            22 => Bound(Decodable::decode(d), Decodable::decode(d)),
-            23 => Placeholder(Decodable::decode(d)),
-            24 => Infer(Decodable::decode(d)),
-            25 => Error(Decodable::decode(d)),
-            _ => panic!(
-                "{}",
-                format!(
-                    "invalid enum variant tag while decoding `{}`, expected 0..{}",
-                    "TyKind", 26,
-                )
-            ),
-        }
-    }
-}
-
 // This is not a derived impl because a derive would require `I: HashStable`
 #[allow(rustc::usage_of_ty_tykind)]
 impl<CTX: HashStableContext, I: Interner> HashStable<CTX> for TyKind<I>