about summary refs log tree commit diff
path: root/compiler/rustc_mir_build/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_mir_build/src')
-rw-r--r--compiler/rustc_mir_build/src/builder/block.rs7
-rw-r--r--compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs1
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_operand.rs2
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_place.rs6
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_temp.rs2
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/into.rs17
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/stmt.rs3
-rw-r--r--compiler/rustc_mir_build/src/builder/matches/match_pair.rs4
-rw-r--r--compiler/rustc_mir_build/src/builder/matches/mod.rs10
-rw-r--r--compiler/rustc_mir_build/src/builder/mod.rs37
-rw-r--r--compiler/rustc_mir_build/src/builder/scope.rs4
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs17
-rw-r--r--compiler/rustc_mir_build/src/errors.rs6
-rw-r--r--compiler/rustc_mir_build/src/thir/constant.rs12
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs14
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/mod.rs51
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs32
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/migration.rs182
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs216
-rw-r--r--compiler/rustc_mir_build/src/thir/print.rs6
-rw-r--r--compiler/rustc_mir_build/src/thir/util.rs2
22 files changed, 348 insertions, 285 deletions
diff --git a/compiler/rustc_mir_build/src/builder/block.rs b/compiler/rustc_mir_build/src/builder/block.rs
index ba63a97de89..7c76e02fcef 100644
--- a/compiler/rustc_mir_build/src/builder/block.rs
+++ b/compiler/rustc_mir_build/src/builder/block.rs
@@ -244,7 +244,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 StmtKind::Let {
                     remainder_scope,
                     init_scope,
-                    ref pattern,
+                    pattern,
                     initializer,
                     lint_level,
                     else_block: None,
@@ -331,8 +331,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             let expr = &this.thir[expr_id];
             let tail_result_is_ignored =
                 destination_ty.is_unit() || this.block_context.currently_ignores_tail_results();
-            this.block_context
-                .push(BlockFrame::TailExpr { tail_result_is_ignored, span: expr.span });
+            this.block_context.push(BlockFrame::TailExpr {
+                info: BlockTailInfo { tail_result_is_ignored, span: expr.span },
+            });
 
             block = this.expr_into_dest(destination, block, expr_id).into_block();
             let popped = this.block_context.pop();
diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs
index eab414e150f..19669021eef 100644
--- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs
+++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs
@@ -1,6 +1,5 @@
 use rustc_abi::{FieldIdx, VariantIdx};
 use rustc_middle::mir::interpret::Scalar;
-use rustc_middle::mir::tcx::PlaceTy;
 use rustc_middle::mir::*;
 use rustc_middle::thir::*;
 use rustc_middle::ty;
diff --git a/compiler/rustc_mir_build/src/builder/expr/as_operand.rs b/compiler/rustc_mir_build/src/builder/expr/as_operand.rs
index 63e9b1dc6cd..2059610ee47 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_operand.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_operand.rs
@@ -142,7 +142,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 // Overwrite temp local info if we have something more interesting to record.
                 if !matches!(local_info, LocalInfo::Boring) {
                     let decl_info =
-                        this.local_decls[operand].local_info.as_mut().assert_crate_local();
+                        this.local_decls[operand].local_info.as_mut().unwrap_crate_local();
                     if let LocalInfo::Boring | LocalInfo::BlockTailTemp(_) = **decl_info {
                         **decl_info = local_info;
                     }
diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs
index 308ca442b40..581f45db6c4 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs
@@ -216,11 +216,11 @@ fn to_upvars_resolved_place_builder<'tcx>(
 /// Supports only HIR projection kinds that represent a path that might be
 /// captured by a closure or a coroutine, i.e., an `Index` or a `Subslice`
 /// projection kinds are unsupported.
-fn strip_prefix<'a, 'tcx>(
+fn strip_prefix<'tcx>(
     mut base_ty: Ty<'tcx>,
-    projections: &'a [PlaceElem<'tcx>],
+    projections: &[PlaceElem<'tcx>],
     prefix_projections: &[HirProjection<'tcx>],
-) -> impl Iterator<Item = PlaceElem<'tcx>> + 'a {
+) -> impl Iterator<Item = PlaceElem<'tcx>> {
     let mut iter = projections
         .iter()
         .copied()
diff --git a/compiler/rustc_mir_build/src/builder/expr/as_temp.rs b/compiler/rustc_mir_build/src/builder/expr/as_temp.rs
index 2927f5b0c45..0bd61168fba 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_temp.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_temp.rs
@@ -85,7 +85,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
                 _ => LocalInfo::Boring,
             };
-            **local_decl.local_info.as_mut().assert_crate_local() = local_info;
+            **local_decl.local_info.as_mut().unwrap_crate_local() = local_info;
             this.local_decls.push(local_decl)
         };
         debug!(?temp);
diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs
index 65dd061003d..72443e2f60d 100644
--- a/compiler/rustc_mir_build/src/builder/expr/into.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/into.rs
@@ -482,15 +482,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                                 }),
                             }
                         }
-                        thir::InlineAsmOperand::SymFn { value, span } => {
-                            mir::InlineAsmOperand::SymFn {
-                                value: Box::new(ConstOperand {
-                                    span,
-                                    user_ty: None,
-                                    const_: value,
-                                }),
-                            }
-                        }
+                        thir::InlineAsmOperand::SymFn { value } => mir::InlineAsmOperand::SymFn {
+                            value: Box::new(this.as_constant(&this.thir[value])),
+                        },
                         thir::InlineAsmOperand::SymStatic { def_id } => {
                             mir::InlineAsmOperand::SymStatic { def_id }
                         }
@@ -518,10 +512,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 }
 
                 let asm_macro = match asm_macro {
-                    AsmMacro::Asm => InlineAsmMacro::Asm,
-                    AsmMacro::GlobalAsm => {
-                        span_bug!(expr_span, "unexpected global_asm! in inline asm")
-                    }
+                    AsmMacro::Asm | AsmMacro::GlobalAsm => InlineAsmMacro::Asm,
                     AsmMacro::NakedAsm => InlineAsmMacro::NakedAsm,
                 };
 
diff --git a/compiler/rustc_mir_build/src/builder/expr/stmt.rs b/compiler/rustc_mir_build/src/builder/expr/stmt.rs
index 58090d3748b..7f8a0a34c31 100644
--- a/compiler/rustc_mir_build/src/builder/expr/stmt.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/stmt.rs
@@ -164,8 +164,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         }
                     }
                     this.block_context.push(BlockFrame::TailExpr {
-                        tail_result_is_ignored: true,
-                        span: expr.span,
+                        info: BlockTailInfo { tail_result_is_ignored: true, span: expr.span },
                     });
                     Some(expr.span)
                 } else {
diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs
index ee331713736..10b43390eb2 100644
--- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs
+++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs
@@ -1,7 +1,7 @@
 use std::sync::Arc;
 
 use rustc_middle::mir::*;
-use rustc_middle::thir::{self, *};
+use rustc_middle::thir::*;
 use rustc_middle::ty::{self, Ty, TypeVisitableExt};
 
 use crate::builder::Builder;
@@ -134,7 +134,7 @@ impl<'tcx> MatchPairTree<'tcx> {
             PatKind::Constant { value } => TestCase::Constant { value },
 
             PatKind::AscribeUserType {
-                ascription: thir::Ascription { ref annotation, variance },
+                ascription: Ascription { ref annotation, variance },
                 ref subpattern,
                 ..
             } => {
diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs
index ed577f7adeb..d05d5b151ff 100644
--- a/compiler/rustc_mir_build/src/builder/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs
@@ -722,7 +722,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     if let LocalInfo::User(BindingForm::Var(VarBindingForm {
                         opt_match_place: Some((ref mut match_place, _)),
                         ..
-                    })) = **self.local_decls[local].local_info.as_mut().assert_crate_local()
+                    })) = **self.local_decls[local].local_info.as_mut().unwrap_crate_local()
                     {
                         *match_place = Some(place);
                     } else {
@@ -926,12 +926,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 // Note that the variance doesn't apply here, as we are tracking the effect
                 // of `user_ty` on any bindings contained with subpattern.
 
-                let projection = UserTypeProjection {
-                    base: self.canonical_user_type_annotations.push(annotation.clone()),
-                    projs: Vec::new(),
-                };
-                let subpattern_user_ty =
-                    pattern_user_ty.push_projection(&projection, annotation.span);
+                let base_user_ty = self.canonical_user_type_annotations.push(annotation.clone());
+                let subpattern_user_ty = pattern_user_ty.push_user_type(base_user_ty);
                 self.visit_primary_bindings(subpattern, subpattern_user_ty, f)
             }
 
diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs
index e04c70b5883..94955954934 100644
--- a/compiler/rustc_mir_build/src/builder/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/mod.rs
@@ -61,7 +61,9 @@ pub fn build_mir<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Body<'tcx> {
         Ok((thir, expr)) => {
             let build_mir = |thir: &Thir<'tcx>| match thir.body_type {
                 thir::BodyTy::Fn(fn_sig) => construct_fn(tcx, def, thir, expr, fn_sig),
-                thir::BodyTy::Const(ty) => construct_const(tcx, def, thir, expr, ty),
+                thir::BodyTy::Const(ty) | thir::BodyTy::GlobalAsm(ty) => {
+                    construct_const(tcx, def, thir, expr, ty)
+                }
             };
 
             // this must run before MIR dump, because
@@ -112,16 +114,7 @@ enum BlockFrame {
     /// Evaluation is currently within the tail expression of a block.
     ///
     /// Example: `{ STMT_1; STMT_2; EXPR }`
-    TailExpr {
-        /// If true, then the surrounding context of the block ignores
-        /// the result of evaluating the block's tail expression.
-        ///
-        /// Example: `let _ = { STMT_1; EXPR };`
-        tail_result_is_ignored: bool,
-
-        /// `Span` of the tail expression.
-        span: Span,
-    },
+    TailExpr { info: BlockTailInfo },
 
     /// Generic mark meaning that the block occurred as a subexpression
     /// where the result might be used.
@@ -277,9 +270,7 @@ impl BlockContext {
             match bf {
                 BlockFrame::SubExpr => continue,
                 BlockFrame::Statement { .. } => break,
-                &BlockFrame::TailExpr { tail_result_is_ignored, span } => {
-                    return Some(BlockTailInfo { tail_result_is_ignored, span });
-                }
+                &BlockFrame::TailExpr { info } => return Some(info),
             }
         }
 
@@ -302,9 +293,9 @@ impl BlockContext {
 
             // otherwise: use accumulated is_ignored state.
             Some(
-                BlockFrame::TailExpr { tail_result_is_ignored: ignored, .. }
-                | BlockFrame::Statement { ignores_expr_result: ignored },
-            ) => *ignored,
+                BlockFrame::TailExpr { info: BlockTailInfo { tail_result_is_ignored: ign, .. } }
+                | BlockFrame::Statement { ignores_expr_result: ign },
+            ) => *ign,
         }
     }
 }
@@ -465,11 +456,10 @@ fn construct_fn<'tcx>(
     assert_eq!(expr.as_usize(), thir.exprs.len() - 1);
 
     // Figure out what primary body this item has.
-    let body = tcx.hir().body_owned_by(fn_def);
+    let body = tcx.hir_body_owned_by(fn_def);
     let span_with_body = tcx.hir().span_with_body(fn_id);
     let return_ty_span = tcx
-        .hir()
-        .fn_decl_by_hir_id(fn_id)
+        .hir_fn_decl_by_hir_id(fn_id)
         .unwrap_or_else(|| span_bug!(span, "can't build MIR for {:?}", fn_def))
         .output
         .span();
@@ -588,6 +578,7 @@ fn construct_const<'a, 'tcx>(
             let span = tcx.def_span(def);
             (span, span)
         }
+        Node::Item(hir::Item { kind: hir::ItemKind::GlobalAsm { .. }, span, .. }) => (*span, *span),
         _ => span_bug!(tcx.def_span(def), "can't build MIR for {:?}", def),
     };
 
@@ -758,7 +749,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         check_overflow |= tcx.sess.overflow_checks();
         // Constants always need overflow checks.
         check_overflow |= matches!(
-            tcx.hir().body_owner_kind(def),
+            tcx.hir_body_owner_kind(def),
             hir::BodyOwnerKind::Const { .. } | hir::BodyOwnerKind::Static(_)
         );
 
@@ -968,7 +959,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 } => {
                     self.local_decls[local].mutability = mutability;
                     self.local_decls[local].source_info.scope = self.source_scope;
-                    **self.local_decls[local].local_info.as_mut().assert_crate_local() =
+                    **self.local_decls[local].local_info.as_mut().unwrap_crate_local() =
                         if let Some(kind) = param.self_kind {
                             LocalInfo::User(BindingForm::ImplicitSelf(kind))
                         } else {
@@ -1033,7 +1024,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let parent_id = self.source_scopes[original_source_scope]
             .local_data
             .as_ref()
-            .assert_crate_local()
+            .unwrap_crate_local()
             .lint_root;
         self.maybe_new_source_scope(pattern_span, arg_hir_id, parent_id);
     }
diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs
index d3551ea3a97..81561239491 100644
--- a/compiler/rustc_mir_build/src/builder/scope.rs
+++ b/compiler/rustc_mir_build/src/builder/scope.rs
@@ -604,7 +604,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let source_scope = self.source_scope;
         if let LintLevel::Explicit(current_hir_id) = lint_level {
             let parent_id =
-                self.source_scopes[source_scope].local_data.as_ref().assert_crate_local().lint_root;
+                self.source_scopes[source_scope].local_data.as_ref().unwrap_crate_local().lint_root;
             self.maybe_new_source_scope(region_scope.1.span, current_hir_id, parent_id);
         }
         self.push_scope(region_scope);
@@ -992,7 +992,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             lint_root: if let LintLevel::Explicit(lint_root) = lint_level {
                 lint_root
             } else {
-                self.source_scopes[parent].local_data.as_ref().assert_crate_local().lint_root
+                self.source_scopes[parent].local_data.as_ref().unwrap_crate_local().lint_root
             },
         };
         self.source_scopes.push(SourceScopeData {
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index 2b9b948763f..c2eafd0a74e 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -2,6 +2,7 @@ use std::borrow::Cow;
 use std::mem;
 use std::ops::Bound;
 
+use rustc_ast::AsmMacro;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::DiagArgValue;
 use rustc_hir::def::DefKind;
@@ -554,12 +555,12 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                             _ => self.requires_unsafe(expr.span, UseOfExternStatic),
                         }
                     }
-                } else if self.thir[arg].ty.is_unsafe_ptr() {
+                } else if self.thir[arg].ty.is_raw_ptr() {
                     self.requires_unsafe(expr.span, DerefOfRawPointer);
                 }
             }
             ExprKind::InlineAsm(box InlineAsmExpr {
-                asm_macro: _,
+                asm_macro: AsmMacro::Asm | AsmMacro::NakedAsm,
                 ref operands,
                 template: _,
                 options: _,
@@ -583,7 +584,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                         }
                         Out { expr: None, reg: _, late: _ }
                         | Const { value: _, span: _ }
-                        | SymFn { value: _, span: _ }
+                        | SymFn { value: _ }
                         | SymStatic { def_id: _ } => {}
                         Label { block } => {
                             // Label blocks are safe context.
@@ -751,7 +752,7 @@ impl UnsafeOpKind {
         span: Span,
         suggest_unsafe_block: bool,
     ) {
-        let parent_id = tcx.hir().get_parent_item(hir_id);
+        let parent_id = tcx.hir_get_parent_item(hir_id);
         let parent_owner = tcx.hir_owner_node(parent_id);
         let should_suggest = parent_owner.fn_sig().is_some_and(|sig| {
             // Do not suggest for safe target_feature functions
@@ -759,7 +760,7 @@ impl UnsafeOpKind {
         });
         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;
+                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,
@@ -921,13 +922,13 @@ impl UnsafeOpKind {
         hir_context: HirId,
         unsafe_op_in_unsafe_fn_allowed: bool,
     ) {
-        let note_non_inherited = tcx.hir().parent_iter(hir_context).find(|(id, node)| {
+        let note_non_inherited = tcx.hir_parent_iter(hir_context).find(|(id, node)| {
             if let hir::Node::Expr(block) = node
                 && let hir::ExprKind::Block(block, _) = block.kind
                 && let hir::BlockCheckMode::UnsafeBlock(_) = block.rules
             {
                 true
-            } else if let Some(sig) = tcx.hir().fn_sig_by_hir_id(*id)
+            } else if let Some(sig) = tcx.hir_fn_sig_by_hir_id(*id)
                 && matches!(sig.header.safety, hir::HeaderSafety::Normal(hir::Safety::Unsafe))
             {
                 true
@@ -1145,7 +1146,7 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
     let thir = &thir.steal();
 
     let hir_id = tcx.local_def_id_to_hir_id(def);
-    let safety_context = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| {
+    let safety_context = tcx.hir_fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| {
         match fn_sig.header.safety {
             // We typeck the body as safe, but otherwise treat it as unsafe everywhere else.
             // Call sites to other SafeTargetFeatures functions are checked explicitly and don't need
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 07bdc59756a..f1753be845d 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -601,8 +601,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNo
             let def_span = self
                 .cx
                 .tcx
-                .hir()
-                .get_if_local(def.did())
+                .hir_get_if_local(def.did())
                 .and_then(|node| node.ident())
                 .map(|ident| ident.span)
                 .unwrap_or_else(|| self.cx.tcx.def_span(def.did()));
@@ -1113,9 +1112,6 @@ pub(crate) struct Rust2024IncompatiblePatSugg {
     pub(crate) suggestion: Vec<(Span, String)>,
     pub(crate) ref_pattern_count: usize,
     pub(crate) binding_mode_count: usize,
-    /// Internal state: the ref-mutability of the default binding mode at the subpattern being
-    /// lowered, with the span where it was introduced. `None` for a by-value default mode.
-    pub(crate) default_mode_span: Option<(Span, ty::Mutability)>,
     /// Labels for where incompatibility-causing by-ref default binding modes were introduced.
     pub(crate) default_mode_labels: FxIndexMap<Span, ty::Mutability>,
 }
diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs
index f303053390c..b3210813703 100644
--- a/compiler/rustc_mir_build/src/thir/constant.rs
+++ b/compiler/rustc_mir_build/src/thir/constant.rs
@@ -48,7 +48,7 @@ pub(crate) fn lit_to_const<'tcx>(
             ty::ValTree::from_raw_bytes(tcx, bytes)
         }
         (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => {
-            ty::ValTree::from_scalar_int((*n).into())
+            ty::ValTree::from_scalar_int(tcx, (*n).into())
         }
         (ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::CStr)) =>
         {
@@ -57,23 +57,23 @@ pub(crate) fn lit_to_const<'tcx>(
         }
         (ast::LitKind::Int(n, _), ty::Uint(ui)) if !neg => {
             let scalar_int = trunc(n.get(), *ui);
-            ty::ValTree::from_scalar_int(scalar_int)
+            ty::ValTree::from_scalar_int(tcx, scalar_int)
         }
         (ast::LitKind::Int(n, _), ty::Int(i)) => {
             let scalar_int = trunc(
                 if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() },
                 i.to_unsigned(),
             );
-            ty::ValTree::from_scalar_int(scalar_int)
+            ty::ValTree::from_scalar_int(tcx, scalar_int)
         }
-        (ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int((*b).into()),
+        (ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int(tcx, (*b).into()),
         (ast::LitKind::Float(n, _), ty::Float(fty)) => {
             let bits = parse_float_into_scalar(*n, *fty, neg).unwrap_or_else(|| {
                 tcx.dcx().bug(format!("couldn't parse float literal: {:?}", lit_input.lit))
             });
-            ty::ValTree::from_scalar_int(bits)
+            ty::ValTree::from_scalar_int(tcx, bits)
         }
-        (ast::LitKind::Char(c), ty::Char) => ty::ValTree::from_scalar_int((*c).into()),
+        (ast::LitKind::Char(c), ty::Char) => ty::ValTree::from_scalar_int(tcx, (*c).into()),
         (ast::LitKind::Err(guar), _) => return ty::Const::new_error(tcx, *guar),
         _ => return ty::Const::new_misc_error(tcx),
     };
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 54da6924db4..88877d05ffa 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -429,9 +429,7 @@ impl<'tcx> ThirBuildCx<'tcx> {
                         let user_provided_types = self.typeck_results.user_provided_types();
                         let user_ty =
                             user_provided_types.get(fun.hir_id).copied().map(|mut u_ty| {
-                                if let ty::UserTypeKind::TypeOf(ref mut did, _) =
-                                    &mut u_ty.value.kind
-                                {
+                                if let ty::UserTypeKind::TypeOf(did, _) = &mut u_ty.value.kind {
                                     *did = adt_def.did();
                                 }
                                 Box::new(u_ty)
@@ -739,13 +737,8 @@ impl<'tcx> ThirBuildCx<'tcx> {
 
                             InlineAsmOperand::Const { value, span }
                         }
-                        hir::InlineAsmOperand::SymFn { ref anon_const } => {
-                            let value =
-                                mir::Const::from_unevaluated(tcx, anon_const.def_id.to_def_id())
-                                    .instantiate_identity();
-                            let span = tcx.def_span(anon_const.def_id);
-
-                            InlineAsmOperand::SymFn { value, span }
+                        hir::InlineAsmOperand::SymFn { expr } => {
+                            InlineAsmOperand::SymFn { value: self.mirror_expr(expr) }
                         }
                         hir::InlineAsmOperand::SymStatic { path: _, def_id } => {
                             InlineAsmOperand::SymStatic { def_id }
@@ -828,7 +821,6 @@ impl<'tcx> ThirBuildCx<'tcx> {
             },
             hir::ExprKind::Match(discr, arms, match_source) => ExprKind::Match {
                 scrutinee: self.mirror_expr(discr),
-                scrutinee_hir_id: discr.hir_id,
                 arms: arms.iter().map(|a| self.convert_arm(a)).collect(),
                 match_source,
             },
diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs
index a01609012b8..2e069cae426 100644
--- a/compiler/rustc_mir_build/src/thir/cx/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs
@@ -22,8 +22,7 @@ pub(crate) fn thir_body(
     tcx: TyCtxt<'_>,
     owner_def: LocalDefId,
 ) -> Result<(&Steal<Thir<'_>>, ExprId), ErrorGuaranteed> {
-    let hir = tcx.hir();
-    let body = hir.body_owned_by(owner_def);
+    let body = tcx.hir_body_owned_by(owner_def);
     let mut cx = ThirBuildCx::new(tcx, owner_def);
     if let Some(reported) = cx.typeck_results.tainted_by_errors {
         return Err(reported);
@@ -31,7 +30,7 @@ pub(crate) fn thir_body(
     let expr = cx.mirror_expr(body.value);
 
     let owner_id = tcx.local_def_id_to_hir_id(owner_def);
-    if let Some(fn_decl) = hir.fn_decl_by_hir_id(owner_id) {
+    if let Some(fn_decl) = tcx.hir_fn_decl_by_hir_id(owner_id) {
         let closure_env_param = cx.closure_env_param(owner_def, owner_id);
         let explicit_params = cx.explicit_params(owner_id, fn_decl, &body);
         cx.thir.params = closure_env_param.into_iter().chain(explicit_params).collect();
@@ -77,23 +76,29 @@ impl<'tcx> ThirBuildCx<'tcx> {
         let hir = tcx.hir();
         let hir_id = tcx.local_def_id_to_hir_id(def);
 
-        let body_type = if hir.body_owner_kind(def).is_fn_or_closure() {
-            // fetch the fully liberated fn signature (that is, all bound
-            // types/lifetimes replaced)
-            BodyTy::Fn(typeck_results.liberated_fn_sigs()[hir_id])
-        } else {
-            // Get the revealed type of this const. This is *not* the adjusted
-            // type of its body, which may be a subtype of this type. For
-            // example:
-            //
-            // fn foo(_: &()) {}
-            // static X: fn(&'static ()) = foo;
-            //
-            // The adjusted type of the body of X is `for<'a> fn(&'a ())` which
-            // is not the same as the type of X. We need the type of the return
-            // place to be the type of the constant because NLL typeck will
-            // equate them.
-            BodyTy::Const(typeck_results.node_type(hir_id))
+        let body_type = match tcx.hir_body_owner_kind(def) {
+            rustc_hir::BodyOwnerKind::Fn | rustc_hir::BodyOwnerKind::Closure => {
+                // fetch the fully liberated fn signature (that is, all bound
+                // types/lifetimes replaced)
+                BodyTy::Fn(typeck_results.liberated_fn_sigs()[hir_id])
+            }
+            rustc_hir::BodyOwnerKind::Const { .. } | rustc_hir::BodyOwnerKind::Static(_) => {
+                // Get the revealed type of this const. This is *not* the adjusted
+                // type of its body, which may be a subtype of this type. For
+                // example:
+                //
+                // fn foo(_: &()) {}
+                // static X: fn(&'static ()) = foo;
+                //
+                // The adjusted type of the body of X is `for<'a> fn(&'a ())` which
+                // is not the same as the type of X. We need the type of the return
+                // place to be the type of the constant because NLL typeck will
+                // equate them.
+                BodyTy::Const(typeck_results.node_type(hir_id))
+            }
+            rustc_hir::BodyOwnerKind::GlobalAsm => {
+                BodyTy::GlobalAsm(typeck_results.node_type(hir_id))
+            }
         };
 
         Self {
@@ -160,12 +165,12 @@ impl<'tcx> ThirBuildCx<'tcx> {
         })
     }
 
-    fn explicit_params<'a>(
-        &'a mut self,
+    fn explicit_params(
+        &mut self,
         owner_id: HirId,
         fn_decl: &'tcx hir::FnDecl<'tcx>,
         body: &'tcx hir::Body<'tcx>,
-    ) -> impl Iterator<Item = Param<'tcx>> + 'a {
+    ) -> impl Iterator<Item = Param<'tcx>> {
         let fn_sig = self.typeck_results.liberated_fn_sigs()[owner_id];
 
         body.params.iter().enumerate().map(move |(index, param)| {
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 697cb7cf37a..d60ae6484af 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -10,7 +10,6 @@ use rustc_hir::{self as hir, BindingMode, ByRef, HirId};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::Level;
 use rustc_middle::bug;
-use rustc_middle::middle::limits::get_limit_size;
 use rustc_middle::thir::visit::Visitor;
 use rustc_middle::thir::*;
 use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -25,7 +24,7 @@ use rustc_session::lint::builtin::{
 };
 use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::hygiene::DesugaringKind;
-use rustc_span::{Ident, Span, sym};
+use rustc_span::{Ident, Span};
 use rustc_trait_selection::infer::InferCtxtExt;
 use tracing::instrument;
 
@@ -152,7 +151,7 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
                 }
                 return;
             }
-            ExprKind::Match { scrutinee, scrutinee_hir_id: _, box ref arms, match_source } => {
+            ExprKind::Match { scrutinee, box ref arms, match_source } => {
                 self.check_match(scrutinee, arms, match_source, ex.span);
             }
             ExprKind::Let { box ref pat, expr } => {
@@ -404,18 +403,11 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
         arms: &[MatchArm<'p, 'tcx>],
         scrut_ty: Ty<'tcx>,
     ) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
-        let pattern_complexity_limit =
-            get_limit_size(cx.tcx.hir().krate_attrs(), cx.tcx.sess, sym::pattern_complexity);
-        let report = rustc_pattern_analysis::rustc::analyze_match(
-            &cx,
-            &arms,
-            scrut_ty,
-            pattern_complexity_limit,
-        )
-        .map_err(|err| {
-            self.error = Err(err);
-            err
-        })?;
+        let report =
+            rustc_pattern_analysis::rustc::analyze_match(&cx, &arms, scrut_ty).map_err(|err| {
+                self.error = Err(err);
+                err
+            })?;
 
         // Warn unreachable subpatterns.
         for (arm, is_useful) in report.arm_usefulness.iter() {
@@ -1049,7 +1041,7 @@ fn find_fallback_pattern_typo<'tcx>(
         let mut imported = vec![];
         let mut imported_spans = vec![];
         let (infcx, param_env) = cx.tcx.infer_ctxt().build_with_typing_env(cx.typing_env);
-        let parent = cx.tcx.hir().get_parent_item(hir_id);
+        let parent = cx.tcx.hir_get_parent_item(hir_id);
 
         for item in cx.tcx.hir_crate_items(()).free_items() {
             if let DefKind::Use = cx.tcx.def_kind(item.owner_id) {
@@ -1105,7 +1097,7 @@ fn find_fallback_pattern_typo<'tcx>(
             }
         }
         if let Some((i, &const_name)) =
-            accessible.iter().enumerate().find(|(_, &const_name)| const_name == name)
+            accessible.iter().enumerate().find(|&(_, &const_name)| const_name == name)
         {
             // The pattern name is an exact match, so the pattern needed to be imported.
             lint.wanted_constant = Some(WantedConstant {
@@ -1123,7 +1115,7 @@ fn find_fallback_pattern_typo<'tcx>(
                 const_path: name.to_string(),
             });
         } else if let Some(i) =
-            imported.iter().enumerate().find(|(_, &const_name)| const_name == name).map(|(i, _)| i)
+            imported.iter().enumerate().find(|&(_, &const_name)| const_name == name).map(|(i, _)| i)
         {
             // The const with the exact name wasn't re-exported from an import in this
             // crate, we point at the import.
@@ -1145,7 +1137,7 @@ fn find_fallback_pattern_typo<'tcx>(
         } else {
             // Look for local bindings for people that might have gotten confused with how
             // `let` and `const` works.
-            for (_, node) in cx.tcx.hir().parent_iter(hir_id) {
+            for (_, node) in cx.tcx.hir_parent_iter(hir_id) {
                 match node {
                     hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Let(let_stmt), .. }) => {
                         if let hir::PatKind::Binding(_, _, binding_name, _) = let_stmt.pat.kind {
@@ -1498,7 +1490,7 @@ fn report_adt_defined_here<'tcx>(
         return None;
     };
     let adt_def_span =
-        tcx.hir().get_if_local(def.did()).and_then(|node| node.ident()).map(|ident| ident.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 {
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index 551ec5cf4e9..667d59d858e 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -148,7 +148,7 @@ impl<'tcx> ConstToPat<'tcx> {
                     if let ty::GenericArgKind::Type(ty) = arg.unpack()
                         && let ty::Param(param_ty) = ty.kind()
                     {
-                        let def_id = self.tcx.hir().enclosing_body_owner(self.id);
+                        let def_id = self.tcx.hir_enclosing_body_owner(self.id);
                         let generics = self.tcx.generics_of(def_id);
                         let param = generics.type_param(*param_ty, self.tcx);
                         let span = self.tcx.def_span(param.def_id);
diff --git a/compiler/rustc_mir_build/src/thir/pattern/migration.rs b/compiler/rustc_mir_build/src/thir/pattern/migration.rs
new file mode 100644
index 00000000000..bd7787b643d
--- /dev/null
+++ b/compiler/rustc_mir_build/src/thir/pattern/migration.rs
@@ -0,0 +1,182 @@
+//! Automatic migration of Rust 2021 patterns to a form valid in both Editions 2021 and 2024.
+
+use rustc_data_structures::fx::FxIndexMap;
+use rustc_errors::MultiSpan;
+use rustc_hir::{BindingMode, ByRef, HirId, Mutability};
+use rustc_lint as lint;
+use rustc_middle::span_bug;
+use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, Ty, TyCtxt};
+use rustc_span::{Ident, Span};
+
+use crate::errors::{Rust2024IncompatiblePat, Rust2024IncompatiblePatSugg};
+use crate::fluent_generated as fluent;
+
+/// For patterns flagged for migration during HIR typeck, this handles constructing and emitting
+/// a diagnostic suggestion.
+pub(super) struct PatMigration<'a> {
+    suggestion: Vec<(Span, String)>,
+    ref_pattern_count: usize,
+    binding_mode_count: usize,
+    /// Internal state: the ref-mutability of the default binding mode at the subpattern being
+    /// lowered, with the span where it was introduced. `None` for a by-value default mode.
+    default_mode_span: Option<(Span, ty::Mutability)>,
+    /// Labels for where incompatibility-causing by-ref default binding modes were introduced.
+    // FIXME(ref_pat_eat_one_layer_2024_structural): To track the default binding mode, we duplicate
+    // logic from HIR typeck (in order to avoid needing to store all changes to the dbm in
+    // TypeckResults). Since the default binding mode acts differently under this feature gate, the
+    // labels will be wrong.
+    default_mode_labels: FxIndexMap<Span, Mutability>,
+    /// Information collected from typeck, including spans for subpatterns invalid in Rust 2024.
+    info: &'a Rust2024IncompatiblePatInfo,
+}
+
+impl<'a> PatMigration<'a> {
+    pub(super) fn new(info: &'a Rust2024IncompatiblePatInfo) -> Self {
+        PatMigration {
+            suggestion: Vec::new(),
+            ref_pattern_count: 0,
+            binding_mode_count: 0,
+            default_mode_span: None,
+            default_mode_labels: Default::default(),
+            info,
+        }
+    }
+
+    /// On Rust 2024, this emits a hard error. On earlier Editions, this emits the
+    /// future-incompatibility lint `rust_2024_incompatible_pat`.
+    pub(super) fn emit<'tcx>(self, tcx: TyCtxt<'tcx>, pat_id: HirId) {
+        let mut spans =
+            MultiSpan::from_spans(self.info.primary_labels.iter().map(|(span, _)| *span).collect());
+        for (span, label) in self.info.primary_labels.iter() {
+            spans.push_span_label(*span, label.clone());
+        }
+        let sugg = Rust2024IncompatiblePatSugg {
+            suggest_eliding_modes: self.info.suggest_eliding_modes,
+            suggestion: self.suggestion,
+            ref_pattern_count: self.ref_pattern_count,
+            binding_mode_count: self.binding_mode_count,
+            default_mode_labels: self.default_mode_labels,
+        };
+        // If a relevant span is from at least edition 2024, this is a hard error.
+        let is_hard_error = spans.primary_spans().iter().any(|span| span.at_least_rust_2024());
+        if is_hard_error {
+            let mut err =
+                tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat);
+            if let Some(info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible {
+                // provide the same reference link as the lint
+                err.note(format!("for more information, see {}", info.reference));
+            }
+            err.arg("bad_modifiers", self.info.bad_modifiers);
+            err.arg("bad_ref_pats", self.info.bad_ref_pats);
+            err.arg("is_hard_error", true);
+            err.subdiagnostic(sugg);
+            err.emit();
+        } else {
+            tcx.emit_node_span_lint(
+                lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
+                pat_id,
+                spans,
+                Rust2024IncompatiblePat {
+                    sugg,
+                    bad_modifiers: self.info.bad_modifiers,
+                    bad_ref_pats: self.info.bad_ref_pats,
+                    is_hard_error,
+                },
+            );
+        }
+    }
+
+    /// Tracks when we're lowering a pattern that implicitly dereferences the scrutinee.
+    /// This should only be called when the pattern type adjustments list `adjustments` is
+    /// non-empty. Returns the prior default binding mode; this should be followed by a call to
+    /// [`PatMigration::leave_ref`] to restore it when we leave the pattern.
+    pub(super) fn visit_implicit_derefs<'tcx>(
+        &mut self,
+        pat_span: Span,
+        adjustments: &[Ty<'tcx>],
+    ) -> Option<(Span, Mutability)> {
+        let implicit_deref_mutbls = adjustments.iter().map(|ref_ty| {
+            let &ty::Ref(_, _, mutbl) = ref_ty.kind() else {
+                span_bug!(pat_span, "pattern implicitly dereferences a non-ref type");
+            };
+            mutbl
+        });
+
+        if !self.info.suggest_eliding_modes {
+            // If we can't fix the pattern by eliding modifiers, we'll need to make the pattern
+            // fully explicit. i.e. we'll need to suggest reference patterns for this.
+            let suggestion_str: String =
+                implicit_deref_mutbls.clone().map(|mutbl| mutbl.ref_prefix_str()).collect();
+            self.suggestion.push((pat_span.shrink_to_lo(), suggestion_str));
+            self.ref_pattern_count += adjustments.len();
+        }
+
+        // Remember if this changed the default binding mode, in case we want to label it.
+        let min_mutbl = implicit_deref_mutbls.min().unwrap();
+        if self.default_mode_span.is_none_or(|(_, old_mutbl)| min_mutbl < old_mutbl) {
+            // This changes the default binding mode to `ref` or `ref mut`. Return the old mode so
+            // it can be reinstated when we leave the pattern.
+            self.default_mode_span.replace((pat_span, min_mutbl))
+        } else {
+            // This does not change the default binding mode; it was already `ref` or `ref mut`.
+            self.default_mode_span
+        }
+    }
+
+    /// Tracks the default binding mode when we're lowering a `&` or `&mut` pattern.
+    /// Returns the prior default binding mode; this should be followed by a call to
+    /// [`PatMigration::leave_ref`] to restore it when we leave the pattern.
+    pub(super) fn visit_explicit_deref(&mut self) -> Option<(Span, Mutability)> {
+        if let Some((default_mode_span, default_ref_mutbl)) = self.default_mode_span {
+            // If this eats a by-ref default binding mode, label the binding mode.
+            self.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
+        }
+        // Set the default binding mode to by-value and return the old default binding mode so it
+        // can be reinstated when we leave the pattern.
+        self.default_mode_span.take()
+    }
+
+    /// Restores the default binding mode after lowering a pattern that could change it.
+    /// This should follow a call to either [`PatMigration::visit_explicit_deref`] or
+    /// [`PatMigration::visit_implicit_derefs`].
+    pub(super) fn leave_ref(&mut self, old_mode_span: Option<(Span, Mutability)>) {
+        self.default_mode_span = old_mode_span
+    }
+
+    /// Determines if a binding is relevant to the diagnostic and adjusts the notes/suggestion if
+    /// so. Bindings are relevant if they have a modifier under a by-ref default mode (invalid in
+    /// Rust 2024) or if we need to suggest a binding modifier for them.
+    pub(super) fn visit_binding(
+        &mut self,
+        pat_span: Span,
+        mode: BindingMode,
+        explicit_ba: BindingMode,
+        ident: Ident,
+    ) {
+        if explicit_ba != BindingMode::NONE
+            && let Some((default_mode_span, default_ref_mutbl)) = self.default_mode_span
+        {
+            // If this overrides a by-ref default binding mode, label the binding mode.
+            self.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
+            // If our suggestion is to elide redundnt modes, this will be one of them.
+            if self.info.suggest_eliding_modes {
+                self.suggestion.push((pat_span.with_hi(ident.span.lo()), String::new()));
+                self.binding_mode_count += 1;
+            }
+        }
+        if !self.info.suggest_eliding_modes
+            && explicit_ba.0 == ByRef::No
+            && let ByRef::Yes(mutbl) = mode.0
+        {
+            // If we can't fix the pattern by eliding modifiers, we'll need to make the pattern
+            // fully explicit. i.e. we'll need to suggest reference patterns for this.
+            let sugg_str = match mutbl {
+                Mutability::Not => "ref ",
+                Mutability::Mut => "ref mut ",
+            };
+            self.suggestion
+                .push((pat_span.with_lo(ident.span.lo()).shrink_to_lo(), sugg_str.to_owned()));
+            self.binding_mode_count += 1;
+        }
+    }
+}
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 83fef7b0de6..8dc3f998e09 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -2,18 +2,17 @@
 
 mod check_match;
 mod const_to_pat;
+mod migration;
 
 use std::cmp::Ordering;
 use std::sync::Arc;
 
 use rustc_abi::{FieldIdx, Integer};
-use rustc_errors::MultiSpan;
 use rustc_errors::codes::*;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::pat_util::EnumerateAndAdjustIterator;
-use rustc_hir::{self as hir, ByRef, Mutability, RangeEnd};
+use rustc_hir::{self as hir, RangeEnd};
 use rustc_index::Idx;
-use rustc_lint as lint;
 use rustc_middle::mir::interpret::LitToConstInput;
 use rustc_middle::thir::{
     Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary,
@@ -26,8 +25,8 @@ use rustc_span::{ErrorGuaranteed, Span};
 use tracing::{debug, instrument};
 
 pub(crate) use self::check_match::check_match;
+use self::migration::PatMigration;
 use crate::errors::*;
-use crate::fluent_generated as fluent;
 
 struct PatCtxt<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
@@ -35,7 +34,7 @@ struct PatCtxt<'a, 'tcx> {
     typeck_results: &'a ty::TypeckResults<'tcx>,
 
     /// Used by the Rust 2024 migration lint.
-    rust_2024_migration_suggestion: Option<Rust2024IncompatiblePatSugg>,
+    rust_2024_migration: Option<PatMigration<'a>>,
 }
 
 pub(super) fn pat_from_hir<'a, 'tcx>(
@@ -44,59 +43,19 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
     typeck_results: &'a ty::TypeckResults<'tcx>,
     pat: &'tcx hir::Pat<'tcx>,
 ) -> Box<Pat<'tcx>> {
-    let migration_info = typeck_results.rust_2024_migration_desugared_pats().get(pat.hir_id);
     let mut pcx = PatCtxt {
         tcx,
         typing_env,
         typeck_results,
-        rust_2024_migration_suggestion: migration_info.and_then(|info| {
-            Some(Rust2024IncompatiblePatSugg {
-                suggest_eliding_modes: info.suggest_eliding_modes,
-                suggestion: Vec::new(),
-                ref_pattern_count: 0,
-                binding_mode_count: 0,
-                default_mode_span: None,
-                default_mode_labels: Default::default(),
-            })
-        }),
+        rust_2024_migration: typeck_results
+            .rust_2024_migration_desugared_pats()
+            .get(pat.hir_id)
+            .map(PatMigration::new),
     };
     let result = pcx.lower_pattern(pat);
     debug!("pat_from_hir({:?}) = {:?}", pat, result);
-    if let Some(info) = migration_info
-        && let Some(sugg) = pcx.rust_2024_migration_suggestion
-    {
-        let mut spans =
-            MultiSpan::from_spans(info.primary_labels.iter().map(|(span, _)| *span).collect());
-        for (span, label) in &info.primary_labels {
-            spans.push_span_label(*span, label.clone());
-        }
-        // If a relevant span is from at least edition 2024, this is a hard error.
-        let is_hard_error = spans.primary_spans().iter().any(|span| span.at_least_rust_2024());
-        if is_hard_error {
-            let mut err =
-                tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat);
-            if let Some(lint_info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible {
-                // provide the same reference link as the lint
-                err.note(format!("for more information, see {}", lint_info.reference));
-            }
-            err.arg("bad_modifiers", info.bad_modifiers);
-            err.arg("bad_ref_pats", info.bad_ref_pats);
-            err.arg("is_hard_error", true);
-            err.subdiagnostic(sugg);
-            err.emit();
-        } else {
-            tcx.emit_node_span_lint(
-                lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
-                pat.hir_id,
-                spans,
-                Rust2024IncompatiblePat {
-                    sugg,
-                    bad_modifiers: info.bad_modifiers,
-                    bad_ref_pats: info.bad_ref_pats,
-                    is_hard_error,
-                },
-            );
-        }
+    if let Some(m) = pcx.rust_2024_migration {
+        m.emit(tcx, pat.hir_id);
     }
     result
 }
@@ -106,31 +65,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         let adjustments: &[Ty<'tcx>] =
             self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
 
+        // Track the default binding mode for the Rust 2024 migration suggestion.
         let mut opt_old_mode_span = None;
-        if let Some(s) = &mut self.rust_2024_migration_suggestion
+        if let Some(s) = &mut self.rust_2024_migration
             && !adjustments.is_empty()
         {
-            let implicit_deref_mutbls = adjustments.iter().map(|ref_ty| {
-                let &ty::Ref(_, _, mutbl) = ref_ty.kind() else {
-                    span_bug!(pat.span, "pattern implicitly dereferences a non-ref type");
-                };
-                mutbl
-            });
-
-            if !s.suggest_eliding_modes {
-                let suggestion_str: String =
-                    implicit_deref_mutbls.clone().map(|mutbl| mutbl.ref_prefix_str()).collect();
-                s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str));
-                s.ref_pattern_count += adjustments.len();
-            }
-
-            // Remember if this changed the default binding mode, in case we want to label it.
-            let min_mutbl = implicit_deref_mutbls.min().unwrap();
-            if s.default_mode_span.is_none_or(|(_, old_mutbl)| min_mutbl < old_mutbl) {
-                opt_old_mode_span = Some(s.default_mode_span);
-                s.default_mode_span = Some((pat.span, min_mutbl));
-            }
-        };
+            opt_old_mode_span = s.visit_implicit_derefs(pat.span, adjustments);
+        }
 
         // When implicit dereferences have been inserted in this pattern, the unadjusted lowered
         // pattern has the type that results *after* dereferencing. For example, in this code:
@@ -169,10 +110,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             })
         });
 
-        if let Some(s) = &mut self.rust_2024_migration_suggestion
-            && let Some(old_mode_span) = opt_old_mode_span
+        if let Some(s) = &mut self.rust_2024_migration
+            && !adjustments.is_empty()
         {
-            s.default_mode_span = old_mode_span;
+            s.leave_ref(opt_old_mode_span);
         }
 
         adjusted_pat
@@ -189,7 +130,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
 
         // Lower the endpoint into a temporary `PatKind` that will then be
         // deconstructed to obtain the constant value and other data.
-        let mut kind: PatKind<'tcx> = self.lower_lit(expr);
+        let mut kind: PatKind<'tcx> = self.lower_pat_expr(expr);
 
         // Unpeel any ascription or inline-const wrapper nodes.
         loop {
@@ -353,7 +294,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
 
             hir::PatKind::Never => PatKind::Never,
 
-            hir::PatKind::Expr(value) => self.lower_lit(value),
+            hir::PatKind::Expr(value) => self.lower_pat_expr(value),
 
             hir::PatKind::Range(ref lo_expr, ref hi_expr, end) => {
                 let (lo_expr, hi_expr) = (lo_expr.as_deref(), hi_expr.as_deref());
@@ -368,16 +309,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             }
             hir::PatKind::Ref(subpattern, _) => {
                 // Track the default binding mode for the Rust 2024 migration suggestion.
-                let old_mode_span = self.rust_2024_migration_suggestion.as_mut().and_then(|s| {
-                    if let Some((default_mode_span, default_ref_mutbl)) = s.default_mode_span {
-                        // If this eats a by-ref default binding mode, label the binding mode.
-                        s.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
-                    }
-                    s.default_mode_span.take()
-                });
+                let opt_old_mode_span =
+                    self.rust_2024_migration.as_mut().and_then(|s| s.visit_explicit_deref());
                 let subpattern = self.lower_pattern(subpattern);
-                if let Some(s) = &mut self.rust_2024_migration_suggestion {
-                    s.default_mode_span = old_mode_span;
+                if let Some(s) = &mut self.rust_2024_migration {
+                    s.leave_ref(opt_old_mode_span);
                 }
                 PatKind::Deref { subpattern }
             }
@@ -408,32 +344,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
                     .get(pat.hir_id)
                     .expect("missing binding mode");
 
-                if let Some(s) = &mut self.rust_2024_migration_suggestion {
-                    if explicit_ba != hir::BindingMode::NONE
-                        && let Some((default_mode_span, default_ref_mutbl)) = s.default_mode_span
-                    {
-                        // If this overrides a by-ref default binding mode, label the binding mode.
-                        s.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
-                        // If our suggestion is to elide redundnt modes, this will be one of them.
-                        if s.suggest_eliding_modes {
-                            s.suggestion.push((pat.span.with_hi(ident.span.lo()), String::new()));
-                            s.binding_mode_count += 1;
-                        }
-                    }
-                    if !s.suggest_eliding_modes
-                        && explicit_ba.0 == ByRef::No
-                        && let ByRef::Yes(mutbl) = mode.0
-                    {
-                        let sugg_str = match mutbl {
-                            Mutability::Not => "ref ",
-                            Mutability::Mut => "ref mut ",
-                        };
-                        s.suggestion.push((
-                            pat.span.with_lo(ident.span.lo()).shrink_to_lo(),
-                            sugg_str.to_owned(),
-                        ));
-                        s.binding_mode_count += 1;
-                    }
+                if let Some(s) = &mut self.rust_2024_migration {
+                    s.visit_binding(pat.span, mode, explicit_ba, ident);
                 }
 
                 // A ref x pattern is the same node used for x, and as such it has
@@ -638,54 +550,57 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         let ty = self.typeck_results.node_type(id);
         let res = self.typeck_results.qpath_res(qpath, id);
 
-        let pat_from_kind = |kind| Box::new(Pat { span, ty, kind });
-
-        let (def_id, is_associated_const) = match res {
-            Res::Def(DefKind::Const, def_id) => (def_id, false),
-            Res::Def(DefKind::AssocConst, def_id) => (def_id, true),
+        let (def_id, user_ty) = match res {
+            Res::Def(DefKind::Const, def_id) => (def_id, None),
+            Res::Def(DefKind::AssocConst, def_id) => {
+                (def_id, self.typeck_results.user_provided_types().get(id))
+            }
 
-            _ => return pat_from_kind(self.lower_variant_or_leaf(res, id, span, ty, vec![])),
+            _ => {
+                // The path isn't the name of a constant, so it must actually
+                // be a unit struct or unit variant (e.g. `Option::None`).
+                let kind = self.lower_variant_or_leaf(res, id, span, ty, vec![]);
+                return Box::new(Pat { span, ty, kind });
+            }
         };
 
+        // Lower the named constant to a THIR pattern.
         let args = self.typeck_results.node_args(id);
         let c = ty::Const::new_unevaluated(self.tcx, ty::UnevaluatedConst { def: def_id, args });
         let subpattern = self.const_to_pat(c, ty, id, span);
-        let pattern = Box::new(Pat {
-            span,
-            ty,
-            kind: PatKind::ExpandedConstant { subpattern, def_id, is_inline: false },
-        });
 
-        if !is_associated_const {
-            return pattern;
-        }
+        // Wrap the pattern in a marker node to indicate that it is the result
+        // of lowering a named constant. This marker is used for improved
+        // diagnostics in some situations, but has no effect at runtime.
+        let mut pattern = {
+            let kind = PatKind::ExpandedConstant { subpattern, def_id, is_inline: false };
+            Box::new(Pat { span, ty, kind })
+        };
 
-        let user_provided_types = self.typeck_results.user_provided_types();
-        if let Some(&user_ty) = user_provided_types.get(id) {
+        // If this is an associated constant with an explicit user-written
+        // type, add an ascription node (e.g. `<Foo<'a> as MyTrait>::CONST`).
+        if let Some(&user_ty) = user_ty {
             let annotation = CanonicalUserTypeAnnotation {
                 user_ty: Box::new(user_ty),
                 span,
                 inferred_ty: self.typeck_results.node_type(id),
             };
-            Box::new(Pat {
-                span,
-                kind: PatKind::AscribeUserType {
-                    subpattern: pattern,
-                    ascription: Ascription {
-                        annotation,
-                        // Note that use `Contravariant` here. See the
-                        // `variance` field documentation for details.
-                        variance: ty::Contravariant,
-                    },
+            let kind = PatKind::AscribeUserType {
+                subpattern: pattern,
+                ascription: Ascription {
+                    annotation,
+                    // Note that we use `Contravariant` here. See the
+                    // `variance` field documentation for details.
+                    variance: ty::Contravariant,
                 },
-                ty,
-            })
-        } else {
-            pattern
+            };
+            pattern = Box::new(Pat { span, kind, ty });
         }
+
+        pattern
     }
 
-    /// Converts inline const patterns.
+    /// Lowers an inline const block (e.g. `const { 1 + 1 }`) to a pattern.
     fn lower_inline_const(
         &mut self,
         block: &'tcx hir::ConstBlock,
@@ -705,14 +620,17 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
 
         let ct = ty::UnevaluatedConst { def: def_id.to_def_id(), args };
         let subpattern = self.const_to_pat(ty::Const::new_unevaluated(self.tcx, ct), ty, id, span);
+
+        // Wrap the pattern in a marker node to indicate that it is the result
+        // of lowering an inline const block.
         PatKind::ExpandedConstant { subpattern, def_id: def_id.to_def_id(), is_inline: true }
     }
 
-    /// Converts literals, paths and negation of literals to patterns.
-    /// The special case for negation exists to allow things like `-128_i8`
-    /// which would overflow if we tried to evaluate `128_i8` and then negate
-    /// afterwards.
-    fn lower_lit(&mut self, expr: &'tcx hir::PatExpr<'tcx>) -> PatKind<'tcx> {
+    /// Lowers the kinds of "expression" that can appear in a HIR pattern:
+    /// - Paths (e.g. `FOO`, `foo::BAR`, `Option::None`)
+    /// - Inline const blocks (e.g. `const { 1 + 1 }`)
+    /// - Literals, possibly negated (e.g. `-128u8`, `"hello"`)
+    fn lower_pat_expr(&mut self, expr: &'tcx hir::PatExpr<'tcx>) -> PatKind<'tcx> {
         let (lit, neg) = match &expr.kind {
             hir::PatExprKind::Path(qpath) => {
                 return self.lower_path(qpath, expr.hir_id, expr.span).kind;
diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs
index 9ab87dd99ff..cd56d93afcf 100644
--- a/compiler/rustc_mir_build/src/thir/print.rs
+++ b/compiler/rustc_mir_build/src/thir/print.rs
@@ -921,10 +921,10 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
                 print_indented!(self, format!("span: {:?}", span), depth_lvl + 1);
                 print_indented!(self, "}", depth_lvl + 1);
             }
-            InlineAsmOperand::SymFn { value, span } => {
+            InlineAsmOperand::SymFn { value } => {
                 print_indented!(self, "InlineAsmOperand::SymFn {", depth_lvl);
-                print_indented!(self, format!("value: {:?}", *value), depth_lvl + 1);
-                print_indented!(self, format!("span: {:?}", span), depth_lvl + 1);
+                print_indented!(self, "value: ", depth_lvl + 1);
+                self.print_expr(*value, depth_lvl + 2);
                 print_indented!(self, "}", depth_lvl + 1);
             }
             InlineAsmOperand::SymStatic { def_id } => {
diff --git a/compiler/rustc_mir_build/src/thir/util.rs b/compiler/rustc_mir_build/src/thir/util.rs
index 4dff093afd0..60a47a94e3a 100644
--- a/compiler/rustc_mir_build/src/thir/util.rs
+++ b/compiler/rustc_mir_build/src/thir/util.rs
@@ -16,7 +16,7 @@ pub(crate) fn user_args_applied_to_ty_of_hir_id<'tcx>(
     let ty = typeck_results.node_type(hir_id);
     match ty.kind() {
         ty::Adt(adt_def, ..) => {
-            if let ty::UserTypeKind::TypeOf(ref mut did, _) = &mut user_ty.value.kind {
+            if let ty::UserTypeKind::TypeOf(did, _) = &mut user_ty.value.kind {
                 *did = adt_def.did();
             }
             Some(user_ty)