about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast/src/ast.rs71
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs8
-rw-r--r--compiler/rustc_ast/src/util/parser.rs24
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs6
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs313
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs23
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs15
-rw-r--r--compiler/rustc_borrowck/src/consumers.rs7
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs15
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/var_name.rs2
-rw-r--r--compiler/rustc_borrowck/src/lib.rs19
-rw-r--r--compiler/rustc_codegen_cranelift/example/gen_block_iterate.rs16
-rw-r--r--compiler/rustc_codegen_cranelift/rustfmt.toml4
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs7
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs5
-rw-r--r--compiler/rustc_const_eval/src/check_consts/check.rs2
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs2
-rw-r--r--compiler/rustc_const_eval/src/const_eval/valtrees.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs43
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs2
-rw-r--r--compiler/rustc_data_structures/src/fx.rs2
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs28
-rw-r--r--compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs4
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs21
-rw-r--r--compiler/rustc_errors/src/json.rs2
-rw-r--r--compiler/rustc_errors/src/lib.rs39
-rw-r--r--compiler/rustc_feature/src/unstable.rs3
-rw-r--r--compiler/rustc_hir/src/hir.rs10
-rw-r--r--compiler/rustc_hir/src/hir_id.rs22
-rw-r--r--compiler/rustc_hir/src/lang_items.rs6
-rw-r--r--compiler/rustc_hir/src/lib.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/check/always_applicable.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs12
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs12
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsicck.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs13
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/mod.rs5
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/collect/item_bounds.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs63
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs245
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs12
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs40
-rw-r--r--compiler/rustc_hir_analysis/src/hir_wf_check.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/mod.rs2
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs15
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/expr_use_visitor.rs16
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs65
-rw-r--r--compiler/rustc_hir_typeck/src/intrinsicck.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs11
-rw-r--r--compiler/rustc_hir_typeck/src/op.rs343
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs11
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs46
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs43
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types/mod.rs7
-rw-r--r--compiler/rustc_interface/src/interface.rs8
-rw-r--r--compiler/rustc_interface/src/passes.rs2
-rw-r--r--compiler/rustc_lint/src/builtin.rs4
-rw-r--r--compiler/rustc_lint/src/context.rs15
-rw-r--r--compiler/rustc_lint/src/impl_trait_overcaptures.rs2
-rw-r--r--compiler/rustc_lint/src/levels.rs124
-rw-r--r--compiler/rustc_lint/src/lib.rs1
-rw-r--r--compiler/rustc_lint/src/non_ascii_idents.rs9
-rw-r--r--compiler/rustc_lint/src/types.rs47
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs57
-rw-r--r--compiler/rustc_metadata/src/creader.rs2
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs10
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs12
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs2
-rw-r--r--compiler/rustc_middle/src/dep_graph/dep_node.rs1
-rw-r--r--compiler/rustc_middle/src/dep_graph/mod.rs1
-rw-r--r--compiler/rustc_middle/src/hir/map.rs54
-rw-r--r--compiler/rustc_middle/src/hir/mod.rs7
-rw-r--r--compiler/rustc_middle/src/lint.rs68
-rw-r--r--compiler/rustc_middle/src/middle/region.rs2
-rw-r--r--compiler/rustc_middle/src/middle/stability.rs2
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs36
-rw-r--r--compiler/rustc_middle/src/query/keys.rs4
-rw-r--r--compiler/rustc_middle/src/query/mod.rs14
-rw-r--r--compiler/rustc_middle/src/thir.rs4
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs2
-rw-r--r--compiler/rustc_middle/src/ty/closure.rs8
-rw-r--r--compiler/rustc_middle/src/ty/context.rs35
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs4
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/parameterized.rs2
-rw-r--r--compiler/rustc_middle/src/ty/trait_def.rs17
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs7
-rw-r--r--compiler/rustc_middle/src/ty/util.rs46
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_place.rs4
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs2
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/stmt.rs10
-rw-r--r--compiler/rustc_mir_build/src/builder/mod.rs14
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs10
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs19
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs4
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs31
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs38
-rw-r--r--compiler/rustc_mir_transform/src/check_const_item_mutation.rs10
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mappings.rs2
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans.rs107
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs18
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs202
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs3
-rw-r--r--compiler/rustc_mir_transform/src/ssa.rs38
-rw-r--r--compiler/rustc_monomorphize/messages.ftl2
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs1
-rw-r--r--compiler/rustc_monomorphize/src/errors.rs1
-rw-r--r--compiler/rustc_next_trait_solver/src/delegate.rs16
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs6
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs3
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs13
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs4
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/trait_goals.rs42
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs14
-rw-r--r--compiler/rustc_parse/src/parser/item.rs32
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs15
-rw-r--r--compiler/rustc_parse/src/parser/token_type.rs4
-rw-r--r--compiler/rustc_passes/src/check_attr.rs6
-rw-r--r--compiler/rustc_passes/src/dead.rs22
-rw-r--r--compiler/rustc_passes/src/diagnostic_items.rs4
-rw-r--r--compiler/rustc_passes/src/stability.rs6
-rw-r--r--compiler/rustc_pattern_analysis/src/lints.rs11
-rw-r--r--compiler/rustc_query_impl/src/plumbing.rs11
-rw-r--r--compiler/rustc_query_system/src/dep_graph/graph.rs10
-rw-r--r--compiler/rustc_query_system/src/dep_graph/mod.rs3
-rw-r--r--compiler/rustc_resolve/src/lib.rs2
-rw-r--r--compiler/rustc_resolve/src/macros.rs15
-rw-r--r--compiler/rustc_session/src/config.rs5
-rw-r--r--compiler/rustc_session/src/options.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs11
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64gc_unknown_hermit.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64gc_unknown_netbsd.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64gc_unknown_none_elf.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv64gc_unknown_openbsd.rs2
-rw-r--r--compiler/rustc_target/src/target_features.rs19
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs6
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs3
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs7
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs10
-rw-r--r--compiler/rustc_trait_selection/src/solve/delegate.rs27
-rw-r--r--compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs35
-rw-r--r--compiler/rustc_trait_selection/src/traits/effects.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs38
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs6
-rw-r--r--compiler/rustc_type_ir/src/interner.rs2
173 files changed, 1922 insertions, 1417 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 33c20602dfd..97e6879c33e 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -981,6 +981,75 @@ impl BinOpKind {
 
 pub type BinOp = Spanned<BinOpKind>;
 
+// Sometimes `BinOpKind` and `AssignOpKind` need the same treatment. The
+// operations covered by `AssignOpKind` are a subset of those covered by
+// `BinOpKind`, so it makes sense to convert `AssignOpKind` to `BinOpKind`.
+impl From<AssignOpKind> for BinOpKind {
+    fn from(op: AssignOpKind) -> BinOpKind {
+        match op {
+            AssignOpKind::AddAssign => BinOpKind::Add,
+            AssignOpKind::SubAssign => BinOpKind::Sub,
+            AssignOpKind::MulAssign => BinOpKind::Mul,
+            AssignOpKind::DivAssign => BinOpKind::Div,
+            AssignOpKind::RemAssign => BinOpKind::Rem,
+            AssignOpKind::BitXorAssign => BinOpKind::BitXor,
+            AssignOpKind::BitAndAssign => BinOpKind::BitAnd,
+            AssignOpKind::BitOrAssign => BinOpKind::BitOr,
+            AssignOpKind::ShlAssign => BinOpKind::Shl,
+            AssignOpKind::ShrAssign => BinOpKind::Shr,
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
+pub enum AssignOpKind {
+    /// The `+=` operator (addition)
+    AddAssign,
+    /// The `-=` operator (subtraction)
+    SubAssign,
+    /// The `*=` operator (multiplication)
+    MulAssign,
+    /// The `/=` operator (division)
+    DivAssign,
+    /// The `%=` operator (modulus)
+    RemAssign,
+    /// The `^=` operator (bitwise xor)
+    BitXorAssign,
+    /// The `&=` operator (bitwise and)
+    BitAndAssign,
+    /// The `|=` operator (bitwise or)
+    BitOrAssign,
+    /// The `<<=` operator (shift left)
+    ShlAssign,
+    /// The `>>=` operator (shift right)
+    ShrAssign,
+}
+
+impl AssignOpKind {
+    pub fn as_str(&self) -> &'static str {
+        use AssignOpKind::*;
+        match self {
+            AddAssign => "+=",
+            SubAssign => "-=",
+            MulAssign => "*=",
+            DivAssign => "/=",
+            RemAssign => "%=",
+            BitXorAssign => "^=",
+            BitAndAssign => "&=",
+            BitOrAssign => "|=",
+            ShlAssign => "<<=",
+            ShrAssign => ">>=",
+        }
+    }
+
+    /// AssignOps are always by value.
+    pub fn is_by_value(self) -> bool {
+        true
+    }
+}
+
+pub type AssignOp = Spanned<AssignOpKind>;
+
 /// Unary operator.
 ///
 /// Note that `&data` is not an operator, it's an `AddrOf` expression.
@@ -1593,7 +1662,7 @@ pub enum ExprKind {
     /// An assignment with an operator.
     ///
     /// E.g., `a += 1`.
-    AssignOp(BinOp, P<Expr>, P<Expr>),
+    AssignOp(AssignOp, P<Expr>, P<Expr>),
     /// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct field.
     Field(P<Expr>, Ident),
     /// An indexing operation (e.g., `foo[2]`).
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 4d613085d79..0b65246693d 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -570,6 +570,14 @@ impl MetaItemInner {
         }
     }
 
+    /// Returns the bool if `self` is a boolean `MetaItemInner::Literal`.
+    pub fn boolean_literal(&self) -> Option<bool> {
+        match self {
+            MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => Some(*b),
+            _ => None,
+        }
+    }
+
     /// Returns the `MetaItem` if `self` is a `MetaItemInner::MetaItem` or if it's
     /// `MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(_), .. })`.
     pub fn meta_item_or_bool(&self) -> Option<&MetaItemInner> {
diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs
index 98b1fc52ed7..1e5f414fae1 100644
--- a/compiler/rustc_ast/src/util/parser.rs
+++ b/compiler/rustc_ast/src/util/parser.rs
@@ -1,6 +1,6 @@
 use rustc_span::kw;
 
-use crate::ast::{self, BinOpKind, RangeLimits};
+use crate::ast::{self, AssignOpKind, BinOpKind, RangeLimits};
 use crate::token::{self, Token};
 
 /// Associative operator.
@@ -9,7 +9,7 @@ pub enum AssocOp {
     /// A binary op.
     Binary(BinOpKind),
     /// `?=` where ? is one of the assignable BinOps
-    AssignOp(BinOpKind),
+    AssignOp(AssignOpKind),
     /// `=`
     Assign,
     /// `as`
@@ -44,16 +44,16 @@ impl AssocOp {
             token::Or => Some(Binary(BinOpKind::BitOr)),
             token::Shl => Some(Binary(BinOpKind::Shl)),
             token::Shr => Some(Binary(BinOpKind::Shr)),
-            token::PlusEq => Some(AssignOp(BinOpKind::Add)),
-            token::MinusEq => Some(AssignOp(BinOpKind::Sub)),
-            token::StarEq => Some(AssignOp(BinOpKind::Mul)),
-            token::SlashEq => Some(AssignOp(BinOpKind::Div)),
-            token::PercentEq => Some(AssignOp(BinOpKind::Rem)),
-            token::CaretEq => Some(AssignOp(BinOpKind::BitXor)),
-            token::AndEq => Some(AssignOp(BinOpKind::BitAnd)),
-            token::OrEq => Some(AssignOp(BinOpKind::BitOr)),
-            token::ShlEq => Some(AssignOp(BinOpKind::Shl)),
-            token::ShrEq => Some(AssignOp(BinOpKind::Shr)),
+            token::PlusEq => Some(AssignOp(AssignOpKind::AddAssign)),
+            token::MinusEq => Some(AssignOp(AssignOpKind::SubAssign)),
+            token::StarEq => Some(AssignOp(AssignOpKind::MulAssign)),
+            token::SlashEq => Some(AssignOp(AssignOpKind::DivAssign)),
+            token::PercentEq => Some(AssignOp(AssignOpKind::RemAssign)),
+            token::CaretEq => Some(AssignOp(AssignOpKind::BitXorAssign)),
+            token::AndEq => Some(AssignOp(AssignOpKind::BitAndAssign)),
+            token::OrEq => Some(AssignOp(AssignOpKind::BitOrAssign)),
+            token::ShlEq => Some(AssignOp(AssignOpKind::ShlAssign)),
+            token::ShrEq => Some(AssignOp(AssignOpKind::ShrAssign)),
             token::Lt => Some(Binary(BinOpKind::Lt)),
             token::Le => Some(Binary(BinOpKind::Le)),
             token::Ge => Some(Binary(BinOpKind::Ge)),
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 52291fdfb30..80bb1e8fc41 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -274,7 +274,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 }
                 ExprKind::Assign(el, er, span) => self.lower_expr_assign(el, er, *span, e.span),
                 ExprKind::AssignOp(op, el, er) => hir::ExprKind::AssignOp(
-                    self.lower_binop(*op),
+                    self.lower_assign_op(*op),
                     self.lower_expr(el),
                     self.lower_expr(er),
                 ),
@@ -443,6 +443,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
         Spanned { node: b.node, span: self.lower_span(b.span) }
     }
 
+    fn lower_assign_op(&mut self, a: AssignOp) -> AssignOp {
+        Spanned { node: a.node, span: self.lower_span(a.span) }
+    }
+
     fn lower_legacy_const_generics(
         &mut self,
         mut f: Expr,
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 839d5d3bb95..9916de8b7b1 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -47,14 +47,14 @@ enum SelfSemantic {
 }
 
 enum TraitOrTraitImpl {
-    Trait { span: Span, constness: Option<Span> },
-    TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref: Span },
+    Trait { span: Span, constness_span: Option<Span> },
+    TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref_span: Span },
 }
 
 impl TraitOrTraitImpl {
     fn constness(&self) -> Option<Span> {
         match self {
-            Self::Trait { constness: Some(span), .. }
+            Self::Trait { constness_span: Some(span), .. }
             | Self::TraitImpl { constness: Const::Yes(span), .. } => Some(*span),
             _ => None,
         }
@@ -66,7 +66,7 @@ struct AstValidator<'a> {
     features: &'a Features,
 
     /// The span of the `extern` in an `extern { ... }` block, if any.
-    extern_mod: Option<Span>,
+    extern_mod_span: Option<Span>,
 
     outer_trait_or_trait_impl: Option<TraitOrTraitImpl>,
 
@@ -75,7 +75,7 @@ struct AstValidator<'a> {
     /// Used to ban nested `impl Trait`, e.g., `impl Into<impl Debug>`.
     /// Nested `impl Trait` _is_ allowed in associated type position,
     /// e.g., `impl Iterator<Item = impl Debug>`.
-    outer_impl_trait: Option<Span>,
+    outer_impl_trait_span: Option<Span>,
 
     disallow_tilde_const: Option<TildeConstReason>,
 
@@ -96,17 +96,22 @@ impl<'a> AstValidator<'a> {
             trait_.map(|(constness, polarity, trait_ref)| TraitOrTraitImpl::TraitImpl {
                 constness,
                 polarity,
-                trait_ref: trait_ref.path.span,
+                trait_ref_span: trait_ref.path.span,
             }),
         );
         f(self);
         self.outer_trait_or_trait_impl = old;
     }
 
-    fn with_in_trait(&mut self, span: Span, constness: Option<Span>, f: impl FnOnce(&mut Self)) {
+    fn with_in_trait(
+        &mut self,
+        span: Span,
+        constness_span: Option<Span>,
+        f: impl FnOnce(&mut Self),
+    ) {
         let old = mem::replace(
             &mut self.outer_trait_or_trait_impl,
-            Some(TraitOrTraitImpl::Trait { span, constness }),
+            Some(TraitOrTraitImpl::Trait { span, constness_span }),
         );
         f(self);
         self.outer_trait_or_trait_impl = old;
@@ -170,10 +175,10 @@ impl<'a> AstValidator<'a> {
         Err(errors::WhereClauseBeforeTypeAlias { span, sugg })
     }
 
-    fn with_impl_trait(&mut self, outer: Option<Span>, f: impl FnOnce(&mut Self)) {
-        let old = mem::replace(&mut self.outer_impl_trait, outer);
+    fn with_impl_trait(&mut self, outer_span: Option<Span>, f: impl FnOnce(&mut Self)) {
+        let old = mem::replace(&mut self.outer_impl_trait_span, outer_span);
         f(self);
-        self.outer_impl_trait = old;
+        self.outer_impl_trait_span = old;
     }
 
     // Mirrors `visit::walk_ty`, but tracks relevant state.
@@ -258,21 +263,22 @@ impl<'a> AstValidator<'a> {
             && let TraitOrTraitImpl::TraitImpl {
                 constness: Const::No,
                 polarity: ImplPolarity::Positive,
-                trait_ref,
+                trait_ref_span,
                 ..
             } = parent
         {
-            Some(trait_ref.shrink_to_lo())
+            Some(trait_ref_span.shrink_to_lo())
         } else {
             None
         };
 
-        let make_trait_const_sugg =
-            if const_trait_impl && let TraitOrTraitImpl::Trait { span, constness: None } = parent {
-                Some(span.shrink_to_lo())
-            } else {
-                None
-            };
+        let make_trait_const_sugg = if const_trait_impl
+            && let TraitOrTraitImpl::Trait { span, constness_span: None } = parent
+        {
+            Some(span.shrink_to_lo())
+        } else {
+            None
+        };
 
         let parent_constness = parent.constness();
         self.dcx().emit_err(errors::TraitFnConst {
@@ -448,13 +454,13 @@ impl<'a> AstValidator<'a> {
         check_where_clause(where_clauses.after);
     }
 
-    fn check_foreign_kind_bodyless(&self, ident: Ident, kind: &str, body: Option<Span>) {
-        let Some(body) = body else {
+    fn check_foreign_kind_bodyless(&self, ident: Ident, kind: &str, body_span: Option<Span>) {
+        let Some(body_span) = body_span else {
             return;
         };
         self.dcx().emit_err(errors::BodyInExtern {
             span: ident.span,
-            body,
+            body: body_span,
             block: self.current_extern_span(),
             kind,
         });
@@ -473,7 +479,7 @@ impl<'a> AstValidator<'a> {
     }
 
     fn current_extern_span(&self) -> Span {
-        self.sess.source_map().guess_head_span(self.extern_mod.unwrap())
+        self.sess.source_map().guess_head_span(self.extern_mod_span.unwrap())
     }
 
     /// An `fn` in `extern { ... }` cannot have qualifiers, e.g. `async fn`.
@@ -583,9 +589,10 @@ impl<'a> AstValidator<'a> {
         self.dcx().emit_err(errors::ModuleNonAscii { span: ident.span, name: ident.name });
     }
 
-    fn deny_generic_params(&self, generics: &Generics, ident: Span) {
+    fn deny_generic_params(&self, generics: &Generics, ident_span: Span) {
         if !generics.params.is_empty() {
-            self.dcx().emit_err(errors::AutoTraitGeneric { span: generics.span, ident });
+            self.dcx()
+                .emit_err(errors::AutoTraitGeneric { span: generics.span, ident: ident_span });
         }
     }
 
@@ -605,11 +612,11 @@ impl<'a> AstValidator<'a> {
         }
     }
 
-    fn deny_items(&self, trait_items: &[P<AssocItem>], ident: Span) {
+    fn deny_items(&self, trait_items: &[P<AssocItem>], ident_span: Span) {
         if !trait_items.is_empty() {
             let spans: Vec<_> = trait_items.iter().map(|i| i.kind.ident().unwrap().span).collect();
             let total = trait_items.first().unwrap().span.to(trait_items.last().unwrap().span);
-            self.dcx().emit_err(errors::AutoTraitItems { spans, total, ident });
+            self.dcx().emit_err(errors::AutoTraitItems { spans, total, ident: ident_span });
         }
     }
 
@@ -694,7 +701,7 @@ impl<'a> AstValidator<'a> {
                 }
             }
             TyKind::ImplTrait(_, bounds) => {
-                if let Some(outer_impl_trait_sp) = self.outer_impl_trait {
+                if let Some(outer_impl_trait_sp) = self.outer_impl_trait_span {
                     self.dcx().emit_err(errors::NestedImplTrait {
                         span: ty.span,
                         outer: outer_impl_trait_sp,
@@ -727,6 +734,19 @@ impl<'a> AstValidator<'a> {
             )
         }
     }
+
+    // Used within `visit_item` for item kinds where we don't call `visit::walk_item`.
+    fn visit_attrs_vis(&mut self, attrs: &'a AttrVec, vis: &'a Visibility) {
+        walk_list!(self, visit_attribute, attrs);
+        self.visit_vis(vis);
+    }
+
+    // Used within `visit_item` for item kinds where we don't call `visit::walk_item`.
+    fn visit_attrs_vis_ident(&mut self, attrs: &'a AttrVec, vis: &'a Visibility, ident: &'a Ident) {
+        walk_list!(self, visit_attribute, attrs);
+        self.visit_vis(vis);
+        self.visit_ident(ident);
+    }
 }
 
 /// Checks that generic parameters are in the correct order,
@@ -834,36 +854,33 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 self_ty,
                 items,
             }) => {
-                self.with_in_trait_impl(Some((*constness, *polarity, t)), |this| {
-                    this.visibility_not_permitted(
-                        &item.vis,
-                        errors::VisibilityNotPermittedNote::TraitImpl,
-                    );
-                    if let TyKind::Dummy = self_ty.kind {
-                        // Abort immediately otherwise the `TyKind::Dummy` will reach HIR lowering,
-                        // which isn't allowed. Not a problem for this obscure, obsolete syntax.
-                        this.dcx().emit_fatal(errors::ObsoleteAuto { span: item.span });
-                    }
-                    if let (&Safety::Unsafe(span), &ImplPolarity::Negative(sp)) = (safety, polarity)
-                    {
-                        this.dcx().emit_err(errors::UnsafeNegativeImpl {
-                            span: sp.to(t.path.span),
-                            negative: sp,
-                            r#unsafe: span,
-                        });
-                    }
+                self.visit_attrs_vis(&item.attrs, &item.vis);
+                self.visibility_not_permitted(
+                    &item.vis,
+                    errors::VisibilityNotPermittedNote::TraitImpl,
+                );
+                if let TyKind::Dummy = self_ty.kind {
+                    // Abort immediately otherwise the `TyKind::Dummy` will reach HIR lowering,
+                    // which isn't allowed. Not a problem for this obscure, obsolete syntax.
+                    self.dcx().emit_fatal(errors::ObsoleteAuto { span: item.span });
+                }
+                if let (&Safety::Unsafe(span), &ImplPolarity::Negative(sp)) = (safety, polarity) {
+                    self.dcx().emit_err(errors::UnsafeNegativeImpl {
+                        span: sp.to(t.path.span),
+                        negative: sp,
+                        r#unsafe: span,
+                    });
+                }
 
-                    this.visit_vis(&item.vis);
-                    let disallowed = matches!(constness, Const::No)
-                        .then(|| TildeConstReason::TraitImpl { span: item.span });
-                    this.with_tilde_const(disallowed, |this| this.visit_generics(generics));
-                    this.visit_trait_ref(t);
-                    this.visit_ty(self_ty);
+                let disallowed = matches!(constness, Const::No)
+                    .then(|| TildeConstReason::TraitImpl { span: item.span });
+                self.with_tilde_const(disallowed, |this| this.visit_generics(generics));
+                self.visit_trait_ref(t);
+                self.visit_ty(self_ty);
 
+                self.with_in_trait_impl(Some((*constness, *polarity, t)), |this| {
                     walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl { of_trait: true });
                 });
-                walk_list!(self, visit_attribute, &item.attrs);
-                return; // Avoid visiting again.
             }
             ItemKind::Impl(box Impl {
                 safety,
@@ -883,39 +900,36 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     only_trait,
                 };
 
-                self.with_in_trait_impl(None, |this| {
-                    this.visibility_not_permitted(
-                        &item.vis,
-                        errors::VisibilityNotPermittedNote::IndividualImplItems,
-                    );
-                    if let &Safety::Unsafe(span) = safety {
-                        this.dcx().emit_err(errors::InherentImplCannotUnsafe {
-                            span: self_ty.span,
-                            annotation_span: span,
-                            annotation: "unsafe",
-                            self_ty: self_ty.span,
-                        });
-                    }
-                    if let &ImplPolarity::Negative(span) = polarity {
-                        this.dcx().emit_err(error(span, "negative", false));
-                    }
-                    if let &Defaultness::Default(def_span) = defaultness {
-                        this.dcx().emit_err(error(def_span, "`default`", true));
-                    }
-                    if let &Const::Yes(span) = constness {
-                        this.dcx().emit_err(error(span, "`const`", true));
-                    }
+                self.visit_attrs_vis(&item.attrs, &item.vis);
+                self.visibility_not_permitted(
+                    &item.vis,
+                    errors::VisibilityNotPermittedNote::IndividualImplItems,
+                );
+                if let &Safety::Unsafe(span) = safety {
+                    self.dcx().emit_err(errors::InherentImplCannotUnsafe {
+                        span: self_ty.span,
+                        annotation_span: span,
+                        annotation: "unsafe",
+                        self_ty: self_ty.span,
+                    });
+                }
+                if let &ImplPolarity::Negative(span) = polarity {
+                    self.dcx().emit_err(error(span, "negative", false));
+                }
+                if let &Defaultness::Default(def_span) = defaultness {
+                    self.dcx().emit_err(error(def_span, "`default`", true));
+                }
+                if let &Const::Yes(span) = constness {
+                    self.dcx().emit_err(error(span, "`const`", true));
+                }
 
-                    this.visit_vis(&item.vis);
-                    this.with_tilde_const(
-                        Some(TildeConstReason::Impl { span: item.span }),
-                        |this| this.visit_generics(generics),
-                    );
-                    this.visit_ty(self_ty);
+                self.with_tilde_const(Some(TildeConstReason::Impl { span: item.span }), |this| {
+                    this.visit_generics(generics)
+                });
+                self.visit_ty(self_ty);
+                self.with_in_trait_impl(None, |this| {
                     walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl { of_trait: false });
                 });
-                walk_list!(self, visit_attribute, &item.attrs);
-                return; // Avoid visiting again.
             }
             ItemKind::Fn(
                 func @ box Fn {
@@ -928,6 +942,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     define_opaque: _,
                 },
             ) => {
+                self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
                 self.check_defaultness(item.span, *defaultness);
 
                 let is_intrinsic =
@@ -955,43 +970,38 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     });
                 }
 
-                self.visit_vis(&item.vis);
-                self.visit_ident(ident);
                 let kind = FnKind::Fn(FnCtxt::Free, &item.vis, &*func);
                 self.visit_fn(kind, item.span, item.id);
-                walk_list!(self, visit_attribute, &item.attrs);
-                return; // Avoid visiting again.
             }
             ItemKind::ForeignMod(ForeignMod { extern_span, abi, safety, .. }) => {
-                self.with_in_extern_mod(*safety, |this| {
-                    let old_item = mem::replace(&mut this.extern_mod, Some(item.span));
-                    this.visibility_not_permitted(
-                        &item.vis,
-                        errors::VisibilityNotPermittedNote::IndividualForeignItems,
-                    );
-
-                    if &Safety::Default == safety {
-                        if item.span.at_least_rust_2024() {
-                            this.dcx().emit_err(errors::MissingUnsafeOnExtern { span: item.span });
-                        } else {
-                            this.lint_buffer.buffer_lint(
-                                MISSING_UNSAFE_ON_EXTERN,
-                                item.id,
-                                item.span,
-                                BuiltinLintDiag::MissingUnsafeOnExtern {
-                                    suggestion: item.span.shrink_to_lo(),
-                                },
-                            );
-                        }
+                let old_item = mem::replace(&mut self.extern_mod_span, Some(item.span));
+                self.visibility_not_permitted(
+                    &item.vis,
+                    errors::VisibilityNotPermittedNote::IndividualForeignItems,
+                );
+
+                if &Safety::Default == safety {
+                    if item.span.at_least_rust_2024() {
+                        self.dcx().emit_err(errors::MissingUnsafeOnExtern { span: item.span });
+                    } else {
+                        self.lint_buffer.buffer_lint(
+                            MISSING_UNSAFE_ON_EXTERN,
+                            item.id,
+                            item.span,
+                            BuiltinLintDiag::MissingUnsafeOnExtern {
+                                suggestion: item.span.shrink_to_lo(),
+                            },
+                        );
                     }
+                }
 
-                    if abi.is_none() {
-                        this.maybe_lint_missing_abi(*extern_span, item.id);
-                    }
+                if abi.is_none() {
+                    self.maybe_lint_missing_abi(*extern_span, item.id);
+                }
+                self.with_in_extern_mod(*safety, |this| {
                     visit::walk_item(this, item);
-                    this.extern_mod = old_item;
                 });
-                return; // Avoid visiting again.
+                self.extern_mod_span = old_item;
             }
             ItemKind::Enum(_, def, _) => {
                 for variant in &def.variants {
@@ -1006,34 +1016,31 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         );
                     }
                 }
+                visit::walk_item(self, item)
             }
             ItemKind::Trait(box Trait { is_auto, generics, ident, bounds, items, .. }) => {
+                self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
                 let is_const_trait =
                     attr::find_by_name(&item.attrs, sym::const_trait).map(|attr| attr.span);
-                self.with_in_trait(item.span, is_const_trait, |this| {
-                    if *is_auto == IsAuto::Yes {
-                        // Auto traits cannot have generics, super traits nor contain items.
-                        this.deny_generic_params(generics, ident.span);
-                        this.deny_super_traits(bounds, ident.span);
-                        this.deny_where_clause(&generics.where_clause, ident.span);
-                        this.deny_items(items, ident.span);
-                    }
+                if *is_auto == IsAuto::Yes {
+                    // Auto traits cannot have generics, super traits nor contain items.
+                    self.deny_generic_params(generics, ident.span);
+                    self.deny_super_traits(bounds, ident.span);
+                    self.deny_where_clause(&generics.where_clause, ident.span);
+                    self.deny_items(items, ident.span);
+                }
 
-                    // Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound
-                    // context for the supertraits.
-                    this.visit_vis(&item.vis);
-                    this.visit_ident(ident);
-                    let disallowed = is_const_trait
-                        .is_none()
-                        .then(|| TildeConstReason::Trait { span: item.span });
-                    this.with_tilde_const(disallowed, |this| {
-                        this.visit_generics(generics);
-                        walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
-                    });
+                // Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound
+                // context for the supertraits.
+                let disallowed =
+                    is_const_trait.is_none().then(|| TildeConstReason::Trait { span: item.span });
+                self.with_tilde_const(disallowed, |this| {
+                    this.visit_generics(generics);
+                    walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
+                });
+                self.with_in_trait(item.span, is_const_trait, |this| {
                     walk_list!(this, visit_assoc_item, items, AssocCtxt::Trait);
                 });
-                walk_list!(self, visit_attribute, &item.attrs);
-                return; // Avoid visiting again
             }
             ItemKind::Mod(safety, ident, mod_kind) => {
                 if let &Safety::Unsafe(span) = safety {
@@ -1045,18 +1052,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 {
                     self.check_mod_file_item_asciionly(*ident);
                 }
+                visit::walk_item(self, item)
             }
             ItemKind::Struct(ident, vdata, generics) => match vdata {
                 VariantData::Struct { fields, .. } => {
-                    self.visit_vis(&item.vis);
-                    self.visit_ident(ident);
+                    self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
                     self.visit_generics(generics);
                     // Permit `Anon{Struct,Union}` as field type.
                     walk_list!(self, visit_struct_field_def, fields);
-                    walk_list!(self, visit_attribute, &item.attrs);
-                    return;
                 }
-                _ => {}
+                _ => visit::walk_item(self, item),
             },
             ItemKind::Union(ident, vdata, generics) => {
                 if vdata.fields().is_empty() {
@@ -1064,15 +1069,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 }
                 match vdata {
                     VariantData::Struct { fields, .. } => {
-                        self.visit_vis(&item.vis);
-                        self.visit_ident(ident);
+                        self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
                         self.visit_generics(generics);
                         // Permit `Anon{Struct,Union}` as field type.
                         walk_list!(self, visit_struct_field_def, fields);
-                        walk_list!(self, visit_attribute, &item.attrs);
-                        return;
                     }
-                    _ => {}
+                    _ => visit::walk_item(self, item),
                 }
             }
             ItemKind::Const(box ConstItem { defaultness, expr, .. }) => {
@@ -1083,6 +1085,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         replace_span: self.ending_semi_or_hi(item.span),
                     });
                 }
+                visit::walk_item(self, item);
             }
             ItemKind::Static(box StaticItem { expr, safety, .. }) => {
                 self.check_item_safety(item.span, *safety);
@@ -1096,6 +1099,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         replace_span: self.ending_semi_or_hi(item.span),
                     });
                 }
+                visit::walk_item(self, item);
             }
             ItemKind::TyAlias(
                 ty_alias @ box TyAlias { defaultness, bounds, where_clauses, ty, .. },
@@ -1119,11 +1123,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         help: self.sess.is_nightly_build(),
                     });
                 }
+                visit::walk_item(self, item);
             }
-            _ => {}
+            _ => visit::walk_item(self, item),
         }
-
-        visit::walk_item(self, item);
     }
 
     fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
@@ -1488,10 +1491,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     || ctxt == AssocCtxt::Trait
                     || matches!(func.sig.header.constness, Const::Yes(_)) =>
             {
-                self.visit_vis(&item.vis);
-                self.visit_ident(&func.ident);
+                self.visit_attrs_vis_ident(&item.attrs, &item.vis, &func.ident);
                 let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), &item.vis, &*func);
-                walk_list!(self, visit_attribute, &item.attrs);
                 self.visit_fn(kind, item.span, item.id);
             }
             AssocItemKind::Type(_) => {
@@ -1596,7 +1597,7 @@ fn deny_equality_constraints(
                     generics.where_clause.span
                 } else {
                     let mut span = predicate_span;
-                    let mut prev: Option<Span> = None;
+                    let mut prev_span: Option<Span> = None;
                     let mut preds = generics.where_clause.predicates.iter().peekable();
                     // Find the predicate that shouldn't have been in the where bound list.
                     while let Some(pred) = preds.next() {
@@ -1606,12 +1607,12 @@ fn deny_equality_constraints(
                             if let Some(next) = preds.peek() {
                                 // This is the first predicate, remove the trailing comma as well.
                                 span = span.with_hi(next.span.lo());
-                            } else if let Some(prev) = prev {
+                            } else if let Some(prev_span) = prev_span {
                                 // Remove the previous comma as well.
-                                span = span.with_lo(prev.hi());
+                                span = span.with_lo(prev_span.hi());
                             }
                         }
-                        prev = Some(pred.span);
+                        prev_span = Some(pred.span);
                     }
                     span
                 };
@@ -1686,10 +1687,10 @@ pub fn check_crate(
     let mut validator = AstValidator {
         sess,
         features,
-        extern_mod: None,
+        extern_mod_span: None,
         outer_trait_or_trait_impl: None,
         has_proc_macro_decls: false,
-        outer_impl_trait: None,
+        outer_impl_trait_span: None,
         disallow_tilde_const: Some(TildeConstReason::Item),
         extern_mod_safety: None,
         lint_buffer: lints,
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index ea60e083c4c..e312f15f05b 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -332,17 +332,19 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
             ast::ExprKind::TryBlock(_) => {
                 gate!(&self, try_blocks, e.span, "`try` expression is experimental");
             }
-            ast::ExprKind::Lit(token::Lit { kind: token::LitKind::Float, suffix, .. }) => {
-                match suffix {
-                    Some(sym::f16) => {
-                        gate!(&self, f16, e.span, "the type `f16` is unstable")
-                    }
-                    Some(sym::f128) => {
-                        gate!(&self, f128, e.span, "the type `f128` is unstable")
-                    }
-                    _ => (),
+            ast::ExprKind::Lit(token::Lit {
+                kind: token::LitKind::Float | token::LitKind::Integer,
+                suffix,
+                ..
+            }) => match suffix {
+                Some(sym::f16) => {
+                    gate!(&self, f16, e.span, "the type `f16` is unstable")
                 }
-            }
+                Some(sym::f128) => {
+                    gate!(&self, f128, e.span, "the type `f128` is unstable")
+                }
+                _ => (),
+            },
             _ => {}
         }
         visit::walk_expr(self, e)
@@ -511,6 +513,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
     gate_all!(contracts, "contracts are incomplete");
     gate_all!(contracts_internals, "contract internal machinery is for internal use only");
     gate_all!(where_clause_attrs, "attributes in `where` clause are unstable");
+    gate_all!(super_let, "`super let` is experimental");
 
     if !visitor.features.never_patterns() {
         if let Some(spans) = spans.get(&sym::never_patterns) {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 7d9dc89bd75..df848a26d39 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -274,22 +274,22 @@ impl<'a> State<'a> {
 
     fn print_expr_binary(
         &mut self,
-        op: ast::BinOp,
+        op: ast::BinOpKind,
         lhs: &ast::Expr,
         rhs: &ast::Expr,
         fixup: FixupContext,
     ) {
-        let binop_prec = op.node.precedence();
+        let binop_prec = op.precedence();
         let left_prec = lhs.precedence();
         let right_prec = rhs.precedence();
 
-        let (mut left_needs_paren, right_needs_paren) = match op.node.fixity() {
+        let (mut left_needs_paren, right_needs_paren) = match op.fixity() {
             Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
             Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec),
             Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec),
         };
 
-        match (&lhs.kind, op.node) {
+        match (&lhs.kind, op) {
             // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is
             // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead
             // of `(x as i32) < ...`. We need to convince it _not_ to do that.
@@ -312,7 +312,7 @@ impl<'a> State<'a> {
 
         self.print_expr_cond_paren(lhs, left_needs_paren, fixup.leftmost_subexpression());
         self.space();
-        self.word_space(op.node.as_str());
+        self.word_space(op.as_str());
         self.print_expr_cond_paren(rhs, right_needs_paren, fixup.subsequent_subexpression());
     }
 
@@ -410,7 +410,7 @@ impl<'a> State<'a> {
                 self.print_expr_method_call(seg, receiver, args, fixup);
             }
             ast::ExprKind::Binary(op, lhs, rhs) => {
-                self.print_expr_binary(*op, lhs, rhs, fixup);
+                self.print_expr_binary(op.node, lhs, rhs, fixup);
             }
             ast::ExprKind::Unary(op, expr) => {
                 self.print_expr_unary(*op, expr, fixup);
@@ -605,8 +605,7 @@ impl<'a> State<'a> {
                     fixup.leftmost_subexpression(),
                 );
                 self.space();
-                self.word(op.node.as_str());
-                self.word_space("=");
+                self.word_space(op.node.as_str());
                 self.print_expr_cond_paren(
                     rhs,
                     rhs.precedence() < ExprPrecedence::Assign,
diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs
index 5a89f7c351c..45cdd232564 100644
--- a/compiler/rustc_borrowck/src/consumers.rs
+++ b/compiler/rustc_borrowck/src/consumers.rs
@@ -1,7 +1,7 @@
 //! This file provides API for compiler consumers.
 
 use rustc_hir::def_id::LocalDefId;
-use rustc_index::{IndexSlice, IndexVec};
+use rustc_index::IndexVec;
 use rustc_middle::mir::{Body, Promoted};
 use rustc_middle::ty::TyCtxt;
 
@@ -100,8 +100,5 @@ pub fn get_body_with_borrowck_facts(
     def: LocalDefId,
     options: ConsumerOptions,
 ) -> BodyWithBorrowckFacts<'_> {
-    let (input_body, promoted) = tcx.mir_promoted(def);
-    let input_body: &Body<'_> = &input_body.borrow();
-    let promoted: &IndexSlice<_, _> = &promoted.borrow();
-    *super::do_mir_borrowck(tcx, input_body, promoted, Some(options)).1.unwrap()
+    *super::do_mir_borrowck(tcx, def, Some(options)).1.unwrap()
 }
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 978186f76a1..1f4eb0c449f 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -181,7 +181,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             let closure = self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
 
             let mut is_loop_move = false;
-            let mut in_pattern = false;
             let mut seen_spans = FxIndexSet::default();
 
             for move_site in &move_site_vec {
@@ -204,7 +203,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     self.suggest_ref_or_clone(
                         mpi,
                         &mut err,
-                        &mut in_pattern,
                         move_spans,
                         moved_place.as_ref(),
                         &mut has_suggest_reborrow,
@@ -256,15 +254,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             let place = &self.move_data.move_paths[mpi].place;
             let ty = place.ty(self.body, self.infcx.tcx).ty;
 
-            // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
-            // Same for if we're in a loop, see #101119.
-            if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
-                if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
-                    // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
-                    self.suggest_reborrow(&mut err, span, moved_place);
-                }
-            }
-
             if self.infcx.param_env.caller_bounds().iter().any(|c| {
                 c.as_trait_clause().is_some_and(|pred| {
                     pred.skip_binder().self_ty() == ty && self.infcx.tcx.is_fn_trait(pred.def_id())
@@ -330,7 +319,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         &self,
         mpi: MovePathIndex,
         err: &mut Diag<'infcx>,
-        in_pattern: &mut bool,
         move_spans: UseSpans<'tcx>,
         moved_place: PlaceRef<'tcx>,
         has_suggest_reborrow: &mut bool,
@@ -545,7 +533,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 && !move_span.is_dummy()
                 && !self.infcx.tcx.sess.source_map().is_imported(move_span)
             {
-                *in_pattern = true;
                 let mut sugg = vec![(pat.span.shrink_to_lo(), "ref ".to_string())];
                 if let Some(pat) = finder.parent_pat {
                     sugg.insert(0, (pat.span.shrink_to_lo(), "ref ".to_string()));
@@ -1364,7 +1351,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         }
         // Try to find predicates on *generic params* that would allow copying `ty`
         let mut suggestion =
-            if let Some(symbol) = tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+            if let Some(symbol) = tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
                 format!(": {symbol}.clone()")
             } else {
                 ".clone()".to_owned()
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index 29cc749877b..0394a42ea9c 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -502,7 +502,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 let upvar = &self.upvars[upvar_field.unwrap().index()];
                 let upvar_hir_id = upvar.get_root_variable();
                 let upvar_name = upvar.to_string(tcx);
-                let upvar_span = tcx.hir().span(upvar_hir_id);
+                let upvar_span = tcx.hir_span(upvar_hir_id);
 
                 let place_name = self.describe_any_place(move_place.as_ref());
 
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 50a18b04de4..d1d783c22e3 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -628,7 +628,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
             if let Some(def_hir) = defined_hir {
                 let upvars_map = self.infcx.tcx.upvars_mentioned(def_id).unwrap();
-                let upvar_def_span = self.infcx.tcx.hir().span(def_hir);
+                let upvar_def_span = self.infcx.tcx.hir_span(def_hir);
                 let upvar_span = upvars_map.get(&def_hir).unwrap().span;
                 diag.subdiagnostic(VarHereDenote::Defined { span: upvar_def_span });
                 diag.subdiagnostic(VarHereDenote::Captured { span: upvar_span });
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 412aaf70c3f..45f5eaa514b 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -291,7 +291,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
         match *error_region {
             ty::ReEarlyParam(ebr) => ebr.has_name().then(|| {
                 let def_id = tcx.generics_of(self.mir_def_id()).region_param(ebr, tcx).def_id;
-                let span = tcx.hir().span_if_local(def_id).unwrap_or(DUMMY_SP);
+                let span = tcx.hir_span_if_local(def_id).unwrap_or(DUMMY_SP);
                 RegionName { name: ebr.name, source: RegionNameSource::NamedEarlyParamRegion(span) }
             }),
 
@@ -302,7 +302,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
             ty::ReLateParam(late_param) => match late_param.kind {
                 ty::LateParamRegionKind::Named(region_def_id, name) => {
                     // Get the span to point to, even if we don't use the name.
-                    let span = tcx.hir().span_if_local(region_def_id).unwrap_or(DUMMY_SP);
+                    let span = tcx.hir_span_if_local(region_def_id).unwrap_or(DUMMY_SP);
                     debug!(
                         "bound region named: {:?}, is_named: {:?}",
                         name,
diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs
index 693d22abbe6..14ed6a27a7a 100644
--- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs
@@ -70,7 +70,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         debug!("get_upvar_name_and_span_for_region: upvar_hir_id={upvar_hir_id:?}");
 
         let upvar_name = tcx.hir_name(upvar_hir_id);
-        let upvar_span = tcx.hir().span(upvar_hir_id);
+        let upvar_span = tcx.hir_span(upvar_hir_id);
         debug!(
             "get_upvar_name_and_span_for_region: upvar_name={upvar_name:?} upvar_span={upvar_span:?}",
         );
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 84b7b8c6a2d..7f0ee28531c 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -103,11 +103,8 @@ pub fn provide(providers: &mut Providers) {
 }
 
 fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
-    let (input_body, promoted) = tcx.mir_promoted(def);
-    debug!("run query mir_borrowck: {}", tcx.def_path_str(def));
-
+    let (input_body, _) = tcx.mir_promoted(def);
     let input_body: &Body<'_> = &input_body.borrow();
-
     if input_body.should_skip() || input_body.tainted_by_errors.is_some() {
         debug!("Skipping borrowck because of injected body or tainted body");
         // Let's make up a borrowck result! Fun times!
@@ -120,7 +117,7 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
         return tcx.arena.alloc(result);
     }
 
-    let borrowck_result = do_mir_borrowck(tcx, input_body, &*promoted.borrow(), None).0;
+    let borrowck_result = do_mir_borrowck(tcx, def, None).0;
     debug!("mir_borrowck done");
 
     tcx.arena.alloc(borrowck_result)
@@ -131,15 +128,16 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
 /// Use `consumer_options: None` for the default behavior of returning
 /// [`BorrowCheckResult`] only. Otherwise, return [`BodyWithBorrowckFacts`] according
 /// to the given [`ConsumerOptions`].
-#[instrument(skip(tcx, input_body, input_promoted), fields(id=?input_body.source.def_id()), level = "debug")]
+#[instrument(skip(tcx), level = "debug")]
 fn do_mir_borrowck<'tcx>(
     tcx: TyCtxt<'tcx>,
-    input_body: &Body<'tcx>,
-    input_promoted: &IndexSlice<Promoted, Body<'tcx>>,
+    def: LocalDefId,
     consumer_options: Option<ConsumerOptions>,
 ) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
-    let def = input_body.source.def_id().expect_local();
     let infcx = BorrowckInferCtxt::new(tcx, def);
+    let (input_body, promoted) = tcx.mir_promoted(def);
+    let input_body: &Body<'_> = &input_body.borrow();
+    let input_promoted: &IndexSlice<_, _> = &promoted.borrow();
     if let Some(e) = input_body.tainted_by_errors {
         infcx.set_tainted_by_errors(e);
     }
@@ -499,7 +497,8 @@ impl<'tcx> BorrowckInferCtxt<'tcx> {
                 )
             });
 
-            self.inject_new_hidden_type_unchecked(key, hidden_ty);
+            let prev = self.register_hidden_type_in_storage(key, hidden_ty);
+            assert_eq!(prev, None);
         }
     }
 }
diff --git a/compiler/rustc_codegen_cranelift/example/gen_block_iterate.rs b/compiler/rustc_codegen_cranelift/example/gen_block_iterate.rs
index 25bfe542d22..de9a3d550ec 100644
--- a/compiler/rustc_codegen_cranelift/example/gen_block_iterate.rs
+++ b/compiler/rustc_codegen_cranelift/example/gen_block_iterate.rs
@@ -6,16 +6,25 @@
 #![feature(gen_blocks)]
 
 fn foo() -> impl Iterator<Item = u32> {
-    gen { yield 42; for x in 3..6 { yield x } }
+    gen {
+        yield 42;
+        for x in 3..6 {
+            yield x
+        }
+    }
 }
 
 fn moved() -> impl Iterator<Item = u32> {
     let mut x = "foo".to_string();
     gen move {
         yield 42;
-        if x == "foo" { return }
+        if x == "foo" {
+            return;
+        }
         x.clear();
-        for x in 3..6 { yield x }
+        for x in 3..6 {
+            yield x
+        }
     }
 }
 
@@ -32,5 +41,4 @@ fn main() {
     let mut iter = moved();
     assert_eq!(iter.next(), Some(42));
     assert_eq!(iter.next(), None);
-
 }
diff --git a/compiler/rustc_codegen_cranelift/rustfmt.toml b/compiler/rustc_codegen_cranelift/rustfmt.toml
index f31fa9c76ab..35c92663eb9 100644
--- a/compiler/rustc_codegen_cranelift/rustfmt.toml
+++ b/compiler/rustc_codegen_cranelift/rustfmt.toml
@@ -1,7 +1,3 @@
-ignore = [
-    "example/gen_block_iterate.rs", # uses edition 2024
-]
-
 # Matches rustfmt.toml of rustc
 style_edition = "2024"
 use_small_heuristics = "Max"
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index 5bf931965c7..4e85286ed55 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -300,6 +300,13 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
         ("sparc", "v8plus") if get_version().0 == 19 => Some(LLVMFeature::new("v9")),
         ("sparc", "v8plus") if get_version().0 < 19 => None,
         ("powerpc", "power8-crypto") => Some(LLVMFeature::new("crypto")),
+        // These new `amx` variants and `movrs` were introduced in LLVM20
+        ("x86", "amx-avx512" | "amx-fp8" | "amx-movrs" | "amx-tf32" | "amx-transpose")
+            if get_version().0 < 20 =>
+        {
+            None
+        }
+        ("x86", "movrs") if get_version().0 < 20 => None,
         (_, s) => Some(LLVMFeature::new(s)),
     }
 }
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 7d411087241..2e91a1d921d 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -959,9 +959,9 @@ fn link_natively(
                 }
             }
 
-            let (level, src) = codegen_results.crate_info.lint_levels.linker_messages;
+            let level = codegen_results.crate_info.lint_levels.linker_messages;
             let lint = |msg| {
-                lint_level(sess, LINKER_MESSAGES, level, src, None, |diag| {
+                lint_level(sess, LINKER_MESSAGES, level, None, |diag| {
                     LinkerOutput { inner: msg }.decorate_lint(diag)
                 })
             };
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index d26d6edf314..f36e42cc6b9 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -34,7 +34,7 @@ use rustc_hir::CRATE_HIR_ID;
 use rustc_hir::def_id::CrateNum;
 use rustc_macros::{Decodable, Encodable, HashStable};
 use rustc_middle::dep_graph::WorkProduct;
-use rustc_middle::lint::LintLevelSource;
+use rustc_middle::lint::LevelAndSource;
 use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
 use rustc_middle::middle::dependency_format::Dependencies;
 use rustc_middle::middle::exported_symbols::SymbolExportKind;
@@ -45,7 +45,6 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 use rustc_session::Session;
 use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT};
 use rustc_session::cstore::{self, CrateSource};
-use rustc_session::lint::Level;
 use rustc_session::lint::builtin::LINKER_MESSAGES;
 use rustc_session::utils::NativeLibKind;
 use rustc_span::Symbol;
@@ -341,7 +340,7 @@ impl CodegenResults {
 /// Instead, encode exactly the information we need.
 #[derive(Copy, Clone, Debug, Encodable, Decodable)]
 pub struct CodegenLintLevels {
-    linker_messages: (Level, LintLevelSource),
+    linker_messages: LevelAndSource,
 }
 
 impl CodegenLintLevels {
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index 90002d3f109..2b74c849f1a 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -335,7 +335,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
             self.tcx.dcx().span_bug(span, "tls access is checked in `Rvalue::ThreadLocalRef`");
         }
         if let Some(def_id) = def_id.as_local()
-            && let Err(guar) = self.tcx.at(span).check_well_formed(hir::OwnerId { def_id })
+            && let Err(guar) = self.tcx.ensure_ok().check_well_formed(hir::OwnerId { def_id })
         {
             self.error_emitted = Some(guar);
         }
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 496f6c86f71..61a7ec13511 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -546,7 +546,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
                         rustc_session::lint::builtin::LONG_RUNNING_CONST_EVAL,
                         hir_id,
                     )
-                    .0
+                    .level
                     .is_error();
                 let span = ecx.cur_span();
                 ecx.tcx.emit_node_span_lint(
diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
index 3776fb55c2e..34239ae1d15 100644
--- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs
+++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
@@ -256,7 +256,7 @@ pub(crate) fn eval_to_valtree<'tcx>(
         Err(err) => {
             let did = cid.instance.def_id();
             let global_const_id = cid.display(tcx);
-            let span = tcx.hir().span_if_local(did);
+            let span = tcx.hir_span_if_local(did);
             match err {
                 ValTreeCreationError::NodesOverflow => {
                     let handled =
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 8f286971e63..d077900587e 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -8,8 +8,9 @@
 
 use std::assert_matches::assert_matches;
 use std::borrow::{Borrow, Cow};
+use std::cell::Cell;
 use std::collections::VecDeque;
-use std::{fmt, mem, ptr};
+use std::{fmt, ptr};
 
 use rustc_abi::{Align, HasDataLayout, Size};
 use rustc_ast::Mutability;
@@ -131,7 +132,7 @@ pub struct Memory<'tcx, M: Machine<'tcx>> {
     /// This stores whether we are currently doing reads purely for the purpose of validation.
     /// Those reads do not trigger the machine's hooks for memory reads.
     /// Needless to say, this must only be set with great care!
-    validation_in_progress: bool,
+    validation_in_progress: Cell<bool>,
 }
 
 /// A reference to some allocation that was already bounds-checked for the given region
@@ -158,7 +159,7 @@ impl<'tcx, M: Machine<'tcx>> Memory<'tcx, M> {
             alloc_map: M::MemoryMap::default(),
             extra_fn_ptr_map: FxIndexMap::default(),
             dead_alloc_map: FxIndexMap::default(),
-            validation_in_progress: false,
+            validation_in_progress: Cell::new(false),
         }
     }
 
@@ -715,7 +716,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         // We want to call the hook on *all* accesses that involve an AllocId, including zero-sized
         // accesses. That means we cannot rely on the closure above or the `Some` branch below. We
         // do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked.
-        if !self.memory.validation_in_progress {
+        if !self.memory.validation_in_progress.get() {
             if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr, size_i64) {
                 M::before_alloc_read(self, alloc_id)?;
             }
@@ -723,7 +724,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
 
         if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc {
             let range = alloc_range(offset, size);
-            if !self.memory.validation_in_progress {
+            if !self.memory.validation_in_progress.get() {
                 M::before_memory_read(
                     self.tcx,
                     &self.machine,
@@ -801,7 +802,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     ) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>>
     {
         let tcx = self.tcx;
-        let validation_in_progress = self.memory.validation_in_progress;
+        let validation_in_progress = self.memory.validation_in_progress.get();
 
         let size_i64 = i64::try_from(size.bytes()).unwrap(); // it would be an error to even ask for more than isize::MAX bytes
         let ptr_and_alloc = Self::check_and_deref_ptr(
@@ -1087,23 +1088,43 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     ///
     /// We do this so Miri's allocation access tracking does not show the validation
     /// reads as spurious accesses.
-    pub fn run_for_validation<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
+    pub fn run_for_validation_mut<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
         // This deliberately uses `==` on `bool` to follow the pattern
         // `assert!(val.replace(new) == old)`.
         assert!(
-            mem::replace(&mut self.memory.validation_in_progress, true) == false,
+            self.memory.validation_in_progress.replace(true) == false,
             "`validation_in_progress` was already set"
         );
         let res = f(self);
         assert!(
-            mem::replace(&mut self.memory.validation_in_progress, false) == true,
+            self.memory.validation_in_progress.replace(false) == true,
+            "`validation_in_progress` was unset by someone else"
+        );
+        res
+    }
+
+    /// Runs the closure in "validation" mode, which means the machine's memory read hooks will be
+    /// suppressed. Needless to say, this must only be set with great care! Cannot be nested.
+    ///
+    /// We do this so Miri's allocation access tracking does not show the validation
+    /// reads as spurious accesses.
+    pub fn run_for_validation_ref<R>(&self, f: impl FnOnce(&Self) -> R) -> R {
+        // This deliberately uses `==` on `bool` to follow the pattern
+        // `assert!(val.replace(new) == old)`.
+        assert!(
+            self.memory.validation_in_progress.replace(true) == false,
+            "`validation_in_progress` was already set"
+        );
+        let res = f(self);
+        assert!(
+            self.memory.validation_in_progress.replace(false) == true,
             "`validation_in_progress` was unset by someone else"
         );
         res
     }
 
     pub(super) fn validation_in_progress(&self) -> bool {
-        self.memory.validation_in_progress
+        self.memory.validation_in_progress.get()
     }
 }
 
@@ -1375,7 +1396,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         };
         let src_alloc = self.get_alloc_raw(src_alloc_id)?;
         let src_range = alloc_range(src_offset, size);
-        assert!(!self.memory.validation_in_progress, "we can't be copying during validation");
+        assert!(!self.memory.validation_in_progress.get(), "we can't be copying during validation");
         // For the overlapping case, it is crucial that we trigger the read hook
         // before the write hook -- the aliasing model cares about the order.
         M::before_memory_read(
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index eb3f552cd27..fb7ba6d7ef5 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -1322,7 +1322,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         trace!("validate_operand_internal: {:?}, {:?}", *val, val.layout.ty);
 
         // Run the visitor.
-        self.run_for_validation(|ecx| {
+        self.run_for_validation_mut(|ecx| {
             let reset_padding = reset_provenance_and_padding && {
                 // Check if `val` is actually stored in memory. If not, padding is not even
                 // represented and we need not reset it.
diff --git a/compiler/rustc_data_structures/src/fx.rs b/compiler/rustc_data_structures/src/fx.rs
index 80e72250470..f0db9623b67 100644
--- a/compiler/rustc_data_structures/src/fx.rs
+++ b/compiler/rustc_data_structures/src/fx.rs
@@ -9,6 +9,8 @@ pub type FxIndexSet<V> = indexmap::IndexSet<V, BuildHasherDefault<FxHasher>>;
 pub type IndexEntry<'a, K, V> = indexmap::map::Entry<'a, K, V>;
 pub type IndexOccupiedEntry<'a, K, V> = indexmap::map::OccupiedEntry<'a, K, V>;
 
+pub use indexmap::set::MutableValues;
+
 #[macro_export]
 macro_rules! define_id_collections {
     ($map_name:ident, $set_name:ident, $entry_name:ident, $key:ty) => {
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 37755e7d61d..a64ffe5da8e 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -691,6 +691,34 @@ fn print_crate_info(
                 };
                 println_info!("{}", passes::get_crate_name(sess, attrs));
             }
+            CrateRootLintLevels => {
+                let Some(attrs) = attrs.as_ref() else {
+                    // no crate attributes, print out an error and exit
+                    return Compilation::Continue;
+                };
+                let crate_name = passes::get_crate_name(sess, attrs);
+                let lint_store = crate::unerased_lint_store(sess);
+                let registered_tools = rustc_resolve::registered_tools_ast(sess.dcx(), attrs);
+                let features = rustc_expand::config::features(sess, attrs, crate_name);
+                let lint_levels = rustc_lint::LintLevelsBuilder::crate_root(
+                    sess,
+                    &features,
+                    true,
+                    lint_store,
+                    &registered_tools,
+                    attrs,
+                );
+                for lint in lint_store.get_lints() {
+                    if let Some(feature_symbol) = lint.feature_gate
+                        && !features.enabled(feature_symbol)
+                    {
+                        // lint is unstable and feature gate isn't active, don't print
+                        continue;
+                    }
+                    let level = lint_levels.lint_level(lint).level;
+                    println_info!("{}={}", lint.name_lower(), level.as_str());
+                }
+            }
             Cfg => {
                 let mut cfgs = sess
                     .psess
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index f0636b600b7..f3aeb8d224b 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -91,13 +91,13 @@ fn annotation_level_for_level(level: Level) -> annotate_snippets::Level {
         Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => {
             annotate_snippets::Level::Error
         }
-        Level::ForceWarning(_) | Level::Warning => annotate_snippets::Level::Warning,
+        Level::ForceWarning | Level::Warning => annotate_snippets::Level::Warning,
         Level::Note | Level::OnceNote => annotate_snippets::Level::Note,
         Level::Help | Level::OnceHelp => annotate_snippets::Level::Help,
         // FIXME(#59346): Not sure how to map this level
         Level::FailureNote => annotate_snippets::Level::Error,
         Level::Allow => panic!("Should not call with Allow"),
-        Level::Expect(_) => panic!("Should not call with Expect"),
+        Level::Expect => panic!("Should not call with Expect"),
     }
 }
 
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 9f4d2ea5c1a..794502d7aae 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -9,7 +9,7 @@ use std::thread::panicking;
 
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_error_messages::{FluentValue, fluent_value_from_str_list_sep_by_and};
-use rustc_lint_defs::Applicability;
+use rustc_lint_defs::{Applicability, LintExpectationId};
 use rustc_macros::{Decodable, Encodable};
 use rustc_span::source_map::Spanned;
 use rustc_span::{DUMMY_SP, Span, Symbol};
@@ -296,6 +296,7 @@ pub struct DiagInner {
 
     pub messages: Vec<(DiagMessage, Style)>,
     pub code: Option<ErrCode>,
+    pub lint_id: Option<LintExpectationId>,
     pub span: MultiSpan,
     pub children: Vec<Subdiag>,
     pub suggestions: Suggestions,
@@ -324,6 +325,7 @@ impl DiagInner {
     pub fn new_with_messages(level: Level, messages: Vec<(DiagMessage, Style)>) -> Self {
         DiagInner {
             level,
+            lint_id: None,
             messages,
             code: None,
             span: MultiSpan::new(),
@@ -346,7 +348,7 @@ impl DiagInner {
         match self.level {
             Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => true,
 
-            Level::ForceWarning(_)
+            Level::ForceWarning
             | Level::Warning
             | Level::Note
             | Level::OnceNote
@@ -354,7 +356,7 @@ impl DiagInner {
             | Level::OnceHelp
             | Level::FailureNote
             | Level::Allow
-            | Level::Expect(_) => false,
+            | Level::Expect => false,
         }
     }
 
@@ -365,7 +367,7 @@ impl DiagInner {
 
     pub(crate) fn is_force_warn(&self) -> bool {
         match self.level {
-            Level::ForceWarning(_) => {
+            Level::ForceWarning => {
                 assert!(self.is_lint.is_some());
                 true
             }
@@ -1259,6 +1261,17 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
         self
     } }
 
+    with_fn! { with_lint_id,
+    /// Add an argument.
+    #[rustc_lint_diagnostics]
+    pub fn lint_id(
+        &mut self,
+        id: LintExpectationId,
+    ) -> &mut Self {
+        self.lint_id = Some(id);
+        self
+    } }
+
     with_fn! { with_primary_message,
     /// Add a primary message.
     #[rustc_lint_diagnostics]
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index 7d7f364fec2..a6583407b7e 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -144,7 +144,7 @@ impl Emitter for JsonEmitter {
                 //
                 // So to avoid ICEs and confused users we "upgrade" the lint level for
                 // those `FutureBreakageItem` to warn.
-                if matches!(diag.level, crate::Level::Allow | crate::Level::Expect(..)) {
+                if matches!(diag.level, crate::Level::Allow | crate::Level::Expect) {
                     diag.level = crate::Level::Warning;
                 }
                 FutureBreakageItem {
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 80e43ede445..f5f7618285e 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -905,8 +905,8 @@ impl<'a> DiagCtxtHandle<'a> {
             DelayedBug => {
                 return self.inner.borrow_mut().emit_diagnostic(diag, self.tainted_with_errors);
             }
-            ForceWarning(_) | Warning | Note | OnceNote | Help | OnceHelp | FailureNote | Allow
-            | Expect(_) => None,
+            ForceWarning | Warning | Note | OnceNote | Help | OnceHelp | FailureNote | Allow
+            | Expect => None,
         };
 
         // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic
@@ -1045,7 +1045,7 @@ impl<'a> DiagCtxtHandle<'a> {
                 // Use `ForceWarning` rather than `Warning` to guarantee emission, e.g. with a
                 // configuration like `--cap-lints allow --force-warn bare_trait_objects`.
                 inner.emit_diagnostic(
-                    DiagInner::new(ForceWarning(None), DiagMessage::Str(warnings)),
+                    DiagInner::new(ForceWarning, DiagMessage::Str(warnings)),
                     None,
                 );
             }
@@ -1450,7 +1450,7 @@ impl<'a> DiagCtxtHandle<'a> {
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_expect(self, msg: impl Into<DiagMessage>, id: LintExpectationId) -> Diag<'a, ()> {
-        Diag::new(self, Expect(id), msg)
+        Diag::new(self, Expect, msg).with_lint_id(id)
     }
 }
 
@@ -1510,7 +1510,7 @@ impl DiagCtxtInner {
             // Future breakages aren't emitted if they're `Level::Allow` or
             // `Level::Expect`, but they still need to be constructed and
             // stashed below, so they'll trigger the must_produce_diag check.
-            assert_matches!(diagnostic.level, Error | Warning | Allow | Expect(_));
+            assert_matches!(diagnostic.level, Error | Warning | Allow | Expect);
             self.future_breakage_diagnostics.push(diagnostic.clone());
         }
 
@@ -1558,7 +1558,7 @@ impl DiagCtxtInner {
                     };
                 }
             }
-            ForceWarning(None) => {} // `ForceWarning(Some(...))` is below, with `Expect`
+            ForceWarning if diagnostic.lint_id.is_none() => {} // `ForceWarning(Some(...))` is below, with `Expect`
             Warning => {
                 if !self.flags.can_emit_warnings {
                     // We are not emitting warnings.
@@ -1580,9 +1580,9 @@ impl DiagCtxtInner {
                 }
                 return None;
             }
-            Expect(expect_id) | ForceWarning(Some(expect_id)) => {
-                self.fulfilled_expectations.insert(expect_id);
-                if let Expect(_) = diagnostic.level {
+            Expect | ForceWarning => {
+                self.fulfilled_expectations.insert(diagnostic.lint_id.unwrap());
+                if let Expect = diagnostic.level {
                     // Nothing emitted here for expected lints.
                     TRACK_DIAGNOSTIC(diagnostic, &mut |_| None);
                     self.suppressed_expected_diag = true;
@@ -1631,7 +1631,7 @@ impl DiagCtxtInner {
 
                 if is_error {
                     self.deduplicated_err_count += 1;
-                } else if matches!(diagnostic.level, ForceWarning(_) | Warning) {
+                } else if matches!(diagnostic.level, ForceWarning | Warning) {
                     self.deduplicated_warn_count += 1;
                 }
                 self.has_printed = true;
@@ -1899,9 +1899,9 @@ pub enum Level {
     /// A `force-warn` lint warning about the code being compiled. Does not prevent compilation
     /// from finishing.
     ///
-    /// The [`LintExpectationId`] is used for expected lint diagnostics. In all other cases this
+    /// Requires a [`LintExpectationId`] for expected lint diagnostics. In all other cases this
     /// should be `None`.
-    ForceWarning(Option<LintExpectationId>),
+    ForceWarning,
 
     /// A warning about the code being compiled. Does not prevent compilation from finishing.
     /// Will be skipped if `can_emit_warnings` is false.
@@ -1926,8 +1926,8 @@ pub enum Level {
     /// Only used for lints.
     Allow,
 
-    /// Only used for lints.
-    Expect(LintExpectationId),
+    /// Only used for lints. Requires a [`LintExpectationId`] for silencing the lints.
+    Expect,
 }
 
 impl fmt::Display for Level {
@@ -1943,7 +1943,7 @@ impl Level {
             Bug | Fatal | Error | DelayedBug => {
                 spec.set_fg(Some(Color::Red)).set_intense(true);
             }
-            ForceWarning(_) | Warning => {
+            ForceWarning | Warning => {
                 spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows));
             }
             Note | OnceNote => {
@@ -1953,7 +1953,7 @@ impl Level {
                 spec.set_fg(Some(Color::Cyan)).set_intense(true);
             }
             FailureNote => {}
-            Allow | Expect(_) => unreachable!(),
+            Allow | Expect => unreachable!(),
         }
         spec
     }
@@ -1962,11 +1962,11 @@ impl Level {
         match self {
             Bug | DelayedBug => "error: internal compiler error",
             Fatal | Error => "error",
-            ForceWarning(_) | Warning => "warning",
+            ForceWarning | Warning => "warning",
             Note | OnceNote => "note",
             Help | OnceHelp => "help",
             FailureNote => "failure-note",
-            Allow | Expect(_) => unreachable!(),
+            Allow | Expect => unreachable!(),
         }
     }
 
@@ -1977,8 +1977,7 @@ impl Level {
     // Can this level be used in a subdiagnostic message?
     fn can_be_subdiag(&self) -> bool {
         match self {
-            Bug | DelayedBug | Fatal | Error | ForceWarning(_) | FailureNote | Allow
-            | Expect(_) => false,
+            Bug | DelayedBug | Fatal | Error | ForceWarning | FailureNote | Allow | Expect => false,
 
             Warning | Note | Help | OnceNote | OnceHelp => true,
         }
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 7c931e32f63..710e129b609 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -324,6 +324,7 @@ declare_features! (
     (unstable, loongarch_target_feature, "1.73.0", Some(44839)),
     (unstable, m68k_target_feature, "1.85.0", Some(134328)),
     (unstable, mips_target_feature, "1.27.0", Some(44839)),
+    (unstable, movrs_target_feature, "CURRENT_RUSTC_VERSION", Some(137976)),
     (unstable, powerpc_target_feature, "1.27.0", Some(44839)),
     (unstable, prfchw_target_feature, "1.78.0", Some(44839)),
     (unstable, riscv_target_feature, "1.45.0", Some(44839)),
@@ -628,6 +629,8 @@ declare_features! (
     (unstable, strict_provenance_lints, "1.61.0", Some(130351)),
     /// Allows string patterns to dereference values to match them.
     (unstable, string_deref_patterns, "1.67.0", Some(87121)),
+    /// Allows `super let` statements.
+    (incomplete, super_let, "CURRENT_RUSTC_VERSION", Some(139076)),
     /// Allows subtrait items to shadow supertrait items.
     (unstable, supertrait_item_shadowing, "1.86.0", Some(89151)),
     /// Allows using `#[thread_local]` on `static` items.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index e3e96894ed1..1a6c15b66a4 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -10,9 +10,9 @@ use rustc_ast::{
     LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind,
 };
 pub use rustc_ast::{
-    AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity,
-    ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto, MetaItemInner, MetaItemLit, Movability,
-    Mutability, UnOp,
+    AssignOp, AssignOpKind, AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind,
+    BoundConstness, BoundPolarity, ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto,
+    MetaItemInner, MetaItemLit, Movability, Mutability, UnOp,
 };
 use rustc_attr_data_structures::AttributeKind;
 use rustc_data_structures::fingerprint::Fingerprint;
@@ -2123,7 +2123,7 @@ pub type Lit = Spanned<LitKind>;
 /// explicit discriminant values for enum variants.
 ///
 /// You can check if this anon const is a default in a const param
-/// `const N: usize = { ... }` with `tcx.hir().opt_const_param_default_param_def_id(..)`
+/// `const N: usize = { ... }` with `tcx.hir_opt_const_param_default_param_def_id(..)`
 #[derive(Copy, Clone, Debug, HashStable_Generic)]
 pub struct AnonConst {
     #[stable_hasher(ignore)]
@@ -2648,7 +2648,7 @@ pub enum ExprKind<'hir> {
     /// An assignment with an operator.
     ///
     /// E.g., `a += 1`.
-    AssignOp(BinOp, &'hir Expr<'hir>, &'hir Expr<'hir>),
+    AssignOp(AssignOp, &'hir Expr<'hir>, &'hir Expr<'hir>),
     /// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct or tuple field.
     Field(&'hir Expr<'hir>, Ident),
     /// An indexing operation (`foo[2]`).
diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs
index 3fa06620ea8..b48a081d371 100644
--- a/compiler/rustc_hir/src/hir_id.rs
+++ b/compiler/rustc_hir/src/hir_id.rs
@@ -83,6 +83,12 @@ pub struct HirId {
     pub local_id: ItemLocalId,
 }
 
+// To ensure correctness of incremental compilation,
+// `HirId` must not implement `Ord` or `PartialOrd`.
+// See https://github.com/rust-lang/rust/issues/90317.
+impl !Ord for HirId {}
+impl !PartialOrd for HirId {}
+
 impl Debug for HirId {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         // Example: HirId(DefId(0:1 ~ aa[7697]::{use#0}).10)
@@ -116,10 +122,6 @@ impl HirId {
     pub fn make_owner(owner: LocalDefId) -> Self {
         Self { owner: OwnerId { def_id: owner }, local_id: ItemLocalId::ZERO }
     }
-
-    pub fn index(self) -> (usize, usize) {
-        (rustc_index::Idx::index(self.owner.def_id), rustc_index::Idx::index(self.local_id))
-    }
 }
 
 impl fmt::Display for HirId {
@@ -128,18 +130,6 @@ impl fmt::Display for HirId {
     }
 }
 
-impl Ord for HirId {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        (self.index()).cmp(&(other.index()))
-    }
-}
-
-impl PartialOrd for HirId {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
 rustc_data_structures::define_stable_id_collections!(HirIdMap, HirIdSet, HirIdMapEntry, HirId);
 rustc_data_structures::define_id_collections!(
     ItemLocalMap,
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index e625514e9ff..90fab01ba2d 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -433,6 +433,12 @@ language_item_table! {
     // Experimental lang items for implementing contract pre- and post-condition checking.
     ContractBuildCheckEnsures, sym::contract_build_check_ensures, contract_build_check_ensures_fn, Target::Fn, GenericRequirement::None;
     ContractCheckRequires,     sym::contract_check_requires,      contract_check_requires_fn,      Target::Fn, GenericRequirement::None;
+
+    // Experimental lang items for `MCP: Low level components for async drop`(https://github.com/rust-lang/compiler-team/issues/727)
+    DefaultTrait4,           sym::default_trait4,      default_trait4_trait,       Target::Trait,          GenericRequirement::None;
+    DefaultTrait3,           sym::default_trait3,      default_trait3_trait,       Target::Trait,          GenericRequirement::None;
+    DefaultTrait2,           sym::default_trait2,      default_trait2_trait,       Target::Trait,          GenericRequirement::None;
+    DefaultTrait1,           sym::default_trait1,      default_trait1_trait,       Target::Trait,          GenericRequirement::None;
 }
 
 /// The requirement imposed on the generics of a lang item
diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs
index 4a839d40571..6bc0f797cca 100644
--- a/compiler/rustc_hir/src/lib.rs
+++ b/compiler/rustc_hir/src/lib.rs
@@ -11,6 +11,7 @@
 #![feature(debug_closure_helpers)]
 #![feature(exhaustive_patterns)]
 #![feature(let_chains)]
+#![feature(negative_impls)]
 #![feature(never_type)]
 #![feature(rustc_attrs)]
 #![feature(variant_count)]
diff --git a/compiler/rustc_hir_analysis/src/check/always_applicable.rs b/compiler/rustc_hir_analysis/src/check/always_applicable.rs
index 8a841a11556..58c3020f60e 100644
--- a/compiler/rustc_hir_analysis/src/check/always_applicable.rs
+++ b/compiler/rustc_hir_analysis/src/check/always_applicable.rs
@@ -36,10 +36,8 @@ use crate::hir::def_id::{DefId, LocalDefId};
 ///    cannot do `struct S<T>; impl<T:Clone> Drop for S<T> { ... }`).
 pub(crate) fn check_drop_impl(
     tcx: TyCtxt<'_>,
-    drop_impl_did: DefId,
+    drop_impl_did: LocalDefId,
 ) -> Result<(), ErrorGuaranteed> {
-    let drop_impl_did = drop_impl_did.expect_local();
-
     match tcx.impl_polarity(drop_impl_did) {
         ty::ImplPolarity::Positive => {}
         ty::ImplPolarity::Negative => {
@@ -56,9 +54,9 @@ pub(crate) fn check_drop_impl(
 
     tcx.ensure_ok().orphan_check_impl(drop_impl_did)?;
 
-    let dtor_impl_trait_ref = tcx.impl_trait_ref(drop_impl_did).unwrap().instantiate_identity();
+    let self_ty = tcx.type_of(drop_impl_did).instantiate_identity();
 
-    match dtor_impl_trait_ref.self_ty().kind() {
+    match self_ty.kind() {
         ty::Adt(adt_def, adt_to_impl_args) => {
             ensure_impl_params_and_item_params_correspond(
                 tcx,
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 07b5837bd87..a0798656763 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -578,10 +578,8 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe
                 }
             }
             _ => {
-                tcx.dcx().span_delayed_bug(
-                    tcx.hir().span(hir_id),
-                    "parameter should have been resolved",
-                );
+                tcx.dcx()
+                    .span_delayed_bug(tcx.hir_span(hir_id), "parameter should have been resolved");
             }
         }
     }
@@ -1049,7 +1047,7 @@ fn check_impl_items_against_trait<'tcx>(
                 leaf_def.as_ref().is_some_and(|node_item| !node_item.defining_node.is_from_trait());
 
             if !is_implemented_here {
-                let full_impl_span = tcx.hir().span_with_body(tcx.local_def_id_to_hir_id(impl_id));
+                let full_impl_span = tcx.hir_span_with_body(tcx.local_def_id_to_hir_id(impl_id));
                 match tcx.eval_default_body_stability(trait_item_id, full_impl_span) {
                     EvalResult::Deny { feature, reason, issue, .. } => default_body_is_unstable(
                         tcx,
@@ -1105,7 +1103,7 @@ fn check_impl_items_against_trait<'tcx>(
         }
 
         if !missing_items.is_empty() {
-            let full_impl_span = tcx.hir().span_with_body(tcx.local_def_id_to_hir_id(impl_id));
+            let full_impl_span = tcx.hir_span_with_body(tcx.local_def_id_to_hir_id(impl_id));
             missing_items_err(tcx, impl_id, &missing_items, full_impl_span);
         }
 
@@ -1321,7 +1319,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
         let typing_env = ty::TypingEnv::non_body_analysis(tcx, field.did);
         let layout = tcx.layout_of(typing_env.as_query_input(ty));
         // We are currently checking the type this field came from, so it must be local
-        let span = tcx.hir().span_if_local(field.did).unwrap();
+        let span = tcx.hir_span_if_local(field.did).unwrap();
         let trivial = layout.is_ok_and(|layout| layout.is_1zst());
         if !trivial {
             return (span, trivial, None);
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 84d07c711fa..aec9518494c 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -1208,7 +1208,7 @@ fn extract_spans_for_error_reporting<'tcx>(
         TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(ExpectedFound { .. }, i) => {
             (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i)))
         }
-        _ => (cause.span, tcx.hir().span_if_local(trait_m.def_id)),
+        _ => (cause.span, tcx.hir_span_if_local(trait_m.def_id)),
     }
 }
 
@@ -1261,7 +1261,7 @@ fn compare_self_type<'tcx>(
                 self_descr
             );
             err.span_label(impl_m_span, format!("`{self_descr}` used in impl"));
-            if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
+            if let Some(span) = tcx.hir_span_if_local(trait_m.def_id) {
                 err.span_label(span, format!("trait method declared without `{self_descr}`"));
             } else {
                 err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
@@ -1281,7 +1281,7 @@ fn compare_self_type<'tcx>(
                 self_descr
             );
             err.span_label(impl_m_span, format!("expected `{self_descr}` in impl"));
-            if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
+            if let Some(span) = tcx.hir_span_if_local(trait_m.def_id) {
                 err.span_label(span, format!("`{self_descr}` used in trait"));
             } else {
                 err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
@@ -1389,7 +1389,7 @@ fn compare_number_of_generics<'tcx>(
                     .collect();
                 (Some(arg_spans), impl_trait_spans)
             } else {
-                let trait_span = tcx.hir().span_if_local(trait_.def_id);
+                let trait_span = tcx.hir_span_if_local(trait_.def_id);
                 (trait_span.map(|s| vec![s]), vec![])
             };
 
@@ -1481,7 +1481,7 @@ fn compare_number_of_method_arguments<'tcx>(
                     }
                 })
             })
-            .or_else(|| tcx.hir().span_if_local(trait_m.def_id));
+            .or_else(|| tcx.hir_span_if_local(trait_m.def_id));
 
         let (impl_m_sig, _) = &tcx.hir_expect_impl_item(impl_m.def_id.expect_local()).expect_fn();
         let pos = impl_number_args.saturating_sub(1);
@@ -2366,7 +2366,7 @@ fn try_report_async_mismatch<'tcx>(
             return Err(tcx.sess.dcx().emit_err(MethodShouldReturnFuture {
                 span: tcx.def_span(impl_m.def_id),
                 method_name: tcx.item_ident(impl_m.def_id),
-                trait_item_span: tcx.hir().span_if_local(trait_m.def_id),
+                trait_item_span: tcx.hir_span_if_local(trait_m.def_id),
             }));
         }
     }
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
index d63165f0f16..32a582aadc1 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
@@ -29,6 +29,7 @@ enum NonAsmTypeReason<'tcx> {
     Invalid(Ty<'tcx>),
     InvalidElement(DefId, Ty<'tcx>),
     NotSizedPtr(Ty<'tcx>),
+    EmptySIMDArray(Ty<'tcx>),
 }
 
 impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
@@ -102,6 +103,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
             }
             ty::Adt(adt, args) if adt.repr().simd() => {
                 let fields = &adt.non_enum_variant().fields;
+                if fields.is_empty() {
+                    return Err(NonAsmTypeReason::EmptySIMDArray(ty));
+                }
                 let field = &fields[FieldIdx::ZERO];
                 let elem_ty = field.ty(self.tcx(), args);
 
@@ -226,6 +230,10 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                             can be used as arguments for inline assembly",
                         ).emit();
                     }
+                    NonAsmTypeReason::EmptySIMDArray(ty) => {
+                        let msg = format!("use of empty SIMD vector `{ty}`");
+                        self.infcx.dcx().struct_span_err(expr.span, msg).emit();
+                    }
                 }
                 return None;
             }
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index d8ae4214527..7c5d7b33a34 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -114,11 +114,11 @@ pub fn provide(providers: &mut Providers) {
 }
 
 fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::Destructor> {
-    tcx.calculate_dtor(def_id.to_def_id(), always_applicable::check_drop_impl)
+    tcx.calculate_dtor(def_id, always_applicable::check_drop_impl)
 }
 
 fn adt_async_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::AsyncDestructor> {
-    tcx.calculate_async_dtor(def_id.to_def_id(), always_applicable::check_drop_impl)
+    tcx.calculate_async_dtor(def_id, always_applicable::check_drop_impl)
 }
 
 /// Given a `DefId` for an opaque type in return position, find its parent item's return
@@ -243,7 +243,7 @@ fn missing_items_err(
             tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(),
         ));
         let code = format!("{padding}{snippet}\n{padding}");
-        if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) {
+        if let Some(span) = tcx.hir_span_if_local(trait_item.def_id) {
             missing_trait_item_label
                 .push(errors::MissingTraitItemLabel { span, item: trait_item.name });
             missing_trait_item.push(errors::MissingTraitItemSuggestion {
@@ -341,9 +341,8 @@ fn bounds_from_generic_predicates<'tcx>(
             ty::ClauseKind::Trait(trait_predicate) => {
                 let entry = types.entry(trait_predicate.self_ty()).or_default();
                 let def_id = trait_predicate.def_id();
-                if Some(def_id) != tcx.lang_items().sized_trait() {
-                    // Type params are `Sized` by default, do not add that restriction to the list
-                    // if it is a positive requirement.
+                if !tcx.is_default_trait(def_id) {
+                    // Do not add that restriction to the list if it is a positive requirement.
                     entry.push(trait_predicate.def_id());
                 }
             }
@@ -534,7 +533,7 @@ fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, sp: Span, d
     let variant_spans: Vec<_> = adt
         .variants()
         .iter()
-        .map(|variant| tcx.hir().span_if_local(variant.def_id).unwrap())
+        .map(|variant| tcx.hir_span_if_local(variant.def_id).unwrap())
         .collect();
     let (mut spans, mut many) = (Vec::new(), None);
     if let [start @ .., end] = &*variant_spans {
diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs
index 15e0a72fdcb..16bac430491 100644
--- a/compiler/rustc_hir_analysis/src/coherence/mod.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs
@@ -153,9 +153,12 @@ pub(crate) fn provide(providers: &mut Providers) {
 }
 
 fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Result<(), ErrorGuaranteed> {
+    let impls = tcx.local_trait_impls(def_id);
     // If there are no impls for the trait, then "all impls" are trivially coherent and we won't check anything
     // anyway. Thus we bail out even before the specialization graph, avoiding the dep_graph edge.
-    let Some(impls) = tcx.all_local_trait_impls(()).get(&def_id) else { return Ok(()) };
+    if impls.is_empty() {
+        return Ok(());
+    }
     // Trigger building the specialization graph for the trait. This will detect and report any
     // overlap errors.
     let mut res = tcx.ensure_ok().specialization_graph_of(def_id);
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index a153ce8ea90..2bed28d7b71 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -116,8 +116,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
                 {
                     // enum variant discriminants are not allowed to use any kind of generics
                     None
-                } else if let Some(param_id) =
-                    tcx.hir().opt_const_param_default_param_def_id(hir_id)
+                } else if let Some(param_id) = tcx.hir_opt_const_param_default_param_def_id(hir_id)
                 {
                     // If the def_id we are calling generics_of on is an anon ct default i.e:
                     //
diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index 6e07f0ff53c..279b1e82a71 100644
--- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -38,13 +38,13 @@ fn associated_type_bounds<'tcx>(
         let icx = ItemCtxt::new(tcx, assoc_item_def_id);
         let mut bounds = Vec::new();
         icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter);
-        // Associated types are implicitly sized unless a `?Sized` bound is found
+        // Implicit bounds are added to associated types unless a `?Trait` bound is found
         match filter {
             PredicateFilter::All
             | PredicateFilter::SelfOnly
             | PredicateFilter::SelfTraitThatDefines(_)
             | PredicateFilter::SelfAndAssociatedTypeBounds => {
-                icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span);
+                icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span);
             }
             // `ConstIfConst` is only interested in `~const` bounds.
             PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
@@ -327,14 +327,13 @@ fn opaque_type_bounds<'tcx>(
         let icx = ItemCtxt::new(tcx, opaque_def_id);
         let mut bounds = Vec::new();
         icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter);
-        // Opaque types are implicitly sized unless a `?Sized` bound is found
+        // Implicit bounds are added to opaque types unless a `?Trait` bound is found
         match filter {
             PredicateFilter::All
             | PredicateFilter::SelfOnly
             | PredicateFilter::SelfTraitThatDefines(_)
             | PredicateFilter::SelfAndAssociatedTypeBounds => {
-                // Associated types are implicitly sized unless a `?Sized` bound is found
-                icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span);
+                icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span);
             }
             //`ConstIfConst` is only interested in `~const` bounds.
             PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 4bd89861a9e..776b23bea8e 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -165,12 +165,42 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
 
             ItemKind::Trait(_, _, _, _, self_bounds, ..)
             | ItemKind::TraitAlias(_, _, self_bounds) => {
-                is_trait = Some(self_bounds);
+                is_trait = Some((self_bounds, item.span));
             }
             _ => {}
         }
     };
 
+    if let Node::TraitItem(item) = node {
+        let parent = tcx.local_parent(item.hir_id().owner.def_id);
+        let Node::Item(parent_trait) = tcx.hir_node_by_def_id(parent) else {
+            unreachable!();
+        };
+
+        let (trait_generics, trait_bounds) = match parent_trait.kind {
+            hir::ItemKind::Trait(_, _, _, generics, supertraits, _) => (generics, supertraits),
+            hir::ItemKind::TraitAlias(_, generics, supertraits) => (generics, supertraits),
+            _ => unreachable!(),
+        };
+
+        // Implicitly add `Self: DefaultAutoTrait` clauses on trait associated items if
+        // they are not added as super trait bounds to the trait itself. See comment on
+        // `requires_default_supertraits` for more details.
+        if !icx.lowerer().requires_default_supertraits(trait_bounds, trait_generics) {
+            let mut bounds = Vec::new();
+            let self_ty_where_predicates = (parent, item.generics.predicates);
+            icx.lowerer().add_default_traits_with_filter(
+                &mut bounds,
+                tcx.types.self_param,
+                &[],
+                Some(self_ty_where_predicates),
+                item.span,
+                |tr| tr != hir::LangItem::Sized,
+            );
+            predicates.extend(bounds);
+        }
+    }
+
     let generics = tcx.generics_of(def_id);
 
     // Below we'll consider the bounds on the type parameters (including `Self`)
@@ -181,11 +211,18 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
         let mut bounds = Vec::new();
         icx.lowerer().lower_bounds(
             tcx.types.self_param,
-            self_bounds,
+            self_bounds.0,
             &mut bounds,
             ty::List::empty(),
             PredicateFilter::All,
         );
+        icx.lowerer().add_default_super_traits(
+            def_id,
+            &mut bounds,
+            self_bounds.0,
+            hir_generics,
+            self_bounds.1,
+        );
         predicates.extend(bounds);
     }
 
@@ -210,8 +247,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
             GenericParamKind::Type { .. } => {
                 let param_ty = icx.lowerer().lower_ty_param(param.hir_id);
                 let mut bounds = Vec::new();
-                // Params are implicitly sized unless a `?Sized` bound is found
-                icx.lowerer().add_sized_bound(
+                // Implicit bounds are added to type params unless a `?Trait` bound is found
+                icx.lowerer().add_default_traits(
                     &mut bounds,
                     param_ty,
                     &[],
@@ -508,7 +545,7 @@ pub(super) fn explicit_predicates_of<'tcx>(
         if matches!(def_kind, DefKind::AnonConst)
             && tcx.features().generic_const_exprs()
             && let Some(defaulted_param_def_id) =
-                tcx.hir().opt_const_param_default_param_def_id(tcx.local_def_id_to_hir_id(def_id))
+                tcx.hir_opt_const_param_default_param_def_id(tcx.local_def_id_to_hir_id(def_id))
         {
             // In `generics_of` we set the generics' parent to be our parent's parent which means that
             // we lose out on the predicates of our actual parent if we dont return those predicates here.
@@ -625,6 +662,22 @@ pub(super) fn implied_predicates_with_filter<'tcx>(
     let self_param_ty = tcx.types.self_param;
     let mut bounds = Vec::new();
     icx.lowerer().lower_bounds(self_param_ty, superbounds, &mut bounds, ty::List::empty(), filter);
+    match filter {
+        PredicateFilter::All
+        | PredicateFilter::SelfOnly
+        | PredicateFilter::SelfTraitThatDefines(_)
+        | PredicateFilter::SelfAndAssociatedTypeBounds => {
+            icx.lowerer().add_default_super_traits(
+                trait_def_id,
+                &mut bounds,
+                superbounds,
+                generics,
+                item.span,
+            );
+        }
+        //`ConstIfConst` is only interested in `~const` bounds.
+        PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
+    }
 
     let where_bounds_that_match =
         icx.probe_ty_param_bounds_in_generics(generics, item.owner_id.def_id, filter);
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 9b0d57bd75b..404753875ee 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -1529,7 +1529,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
             if let ResolvedArg::LateBound(..) = def
                 && let Some(what) = crossed_late_boundary
             {
-                let use_span = self.tcx.hir().span(hir_id);
+                let use_span = self.tcx.hir_span(hir_id);
                 let def_span = self.tcx.def_span(param_def_id);
                 let guar = match self.tcx.def_kind(param_def_id) {
                     DefKind::ConstParam => {
@@ -1576,11 +1576,11 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                 } => {
                     let guar = self.tcx.dcx().emit_err(match self.tcx.def_kind(param_def_id) {
                         DefKind::TyParam => errors::LateBoundInApit::Type {
-                            span: self.tcx.hir().span(hir_id),
+                            span: self.tcx.hir_span(hir_id),
                             param_span: self.tcx.def_span(param_def_id),
                         },
                         DefKind::ConstParam => errors::LateBoundInApit::Const {
-                            span: self.tcx.hir().span(hir_id),
+                            span: self.tcx.hir_span(hir_id),
                             param_span: self.tcx.def_span(param_def_id),
                         },
                         kind => {
@@ -1605,7 +1605,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
 
         self.tcx
             .dcx()
-            .span_bug(self.tcx.hir().span(hir_id), format!("could not resolve {param_def_id:?}"));
+            .span_bug(self.tcx.hir_span(hir_id), format!("could not resolve {param_def_id:?}"));
     }
 
     #[instrument(level = "debug", skip(self))]
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index f0dffd780bc..c3bb860538e 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -4,9 +4,9 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_errors::codes::*;
 use rustc_errors::struct_span_code_err;
 use rustc_hir as hir;
-use rustc_hir::HirId;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::{AmbigArg, HirId};
 use rustc_middle::bug;
 use rustc_middle::ty::{
     self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
@@ -24,25 +24,190 @@ use crate::hir_ty_lowering::{
 };
 
 impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
-    /// Add a `Sized` bound to the `bounds` if appropriate.
-    ///
-    /// Doesn't add the bound if the HIR bounds contain any of `Sized`, `?Sized` or `!Sized`.
-    pub(crate) fn add_sized_bound(
+    pub(crate) fn add_default_traits(
         &self,
         bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
         self_ty: Ty<'tcx>,
+        hir_bounds: &[hir::GenericBound<'tcx>],
+        self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+        span: Span,
+    ) {
+        self.add_default_traits_with_filter(
+            bounds,
+            self_ty,
+            hir_bounds,
+            self_ty_where_predicates,
+            span,
+            |_| true,
+        );
+    }
+
+    /// Checks whether `Self: DefaultAutoTrait` bounds should be added on trait super bounds
+    /// or associative items.
+    ///
+    /// To keep backward compatibility with existing code, `experimental_default_bounds` bounds
+    /// should be added everywhere, including super bounds. However this causes a huge performance
+    /// costs. For optimization purposes instead of adding default supertraits, bounds
+    /// are added to the associative items:
+    ///
+    /// ```ignore(illustrative)
+    /// // Default bounds are generated in the following way:
+    /// trait Trait {
+    ///     fn foo(&self) where Self: Leak {}
+    /// }
+    ///
+    /// // instead of this:
+    /// trait Trait: Leak {
+    ///     fn foo(&self) {}
+    /// }
+    /// ```
+    /// It is not always possible to do this because of backward compatibility:
+    ///
+    /// ```ignore(illustrative)
+    /// pub trait Trait<Rhs = Self> {}
+    /// pub trait Trait1 : Trait {}
+    /// //~^ ERROR: `Rhs` requires `DefaultAutoTrait`, but `Self` is not `DefaultAutoTrait`
+    /// ```
+    ///
+    /// or:
+    ///
+    /// ```ignore(illustrative)
+    /// trait Trait {
+    ///     type Type where Self: Sized;
+    /// }
+    /// trait Trait2<T> : Trait<Type = T> {}
+    /// //~^ ERROR: `DefaultAutoTrait` required for `Trait2`, by implicit  `Self: DefaultAutoTrait` in `Trait::Type`
+    /// ```
+    ///
+    /// Therefore, `experimental_default_bounds` are still being added to supertraits if
+    /// the `SelfTyParam` or `AssocItemConstraint` were found in a trait header.
+    pub(crate) fn requires_default_supertraits(
+        &self,
         hir_bounds: &'tcx [hir::GenericBound<'tcx>],
+        hir_generics: &'tcx hir::Generics<'tcx>,
+    ) -> bool {
+        struct TraitInfoCollector;
+
+        impl<'tcx> hir::intravisit::Visitor<'tcx> for TraitInfoCollector {
+            type Result = ControlFlow<()>;
+
+            fn visit_assoc_item_constraint(
+                &mut self,
+                _constraint: &'tcx hir::AssocItemConstraint<'tcx>,
+            ) -> Self::Result {
+                ControlFlow::Break(())
+            }
+
+            fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result {
+                if matches!(
+                    &t.kind,
+                    hir::TyKind::Path(hir::QPath::Resolved(
+                        _,
+                        hir::Path { res: hir::def::Res::SelfTyParam { .. }, .. },
+                    ))
+                ) {
+                    return ControlFlow::Break(());
+                }
+                hir::intravisit::walk_ty(self, t)
+            }
+        }
+
+        let mut found = false;
+        for bound in hir_bounds {
+            found |= hir::intravisit::walk_param_bound(&mut TraitInfoCollector, bound).is_break();
+        }
+        found |= hir::intravisit::walk_generics(&mut TraitInfoCollector, hir_generics).is_break();
+        found
+    }
+
+    /// Lazily sets `experimental_default_bounds` to true on trait super bounds.
+    /// See `requires_default_supertraits` for more information.
+    pub(crate) fn add_default_super_traits(
+        &self,
+        trait_def_id: LocalDefId,
+        bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
+        hir_bounds: &'tcx [hir::GenericBound<'tcx>],
+        hir_generics: &'tcx hir::Generics<'tcx>,
+        span: Span,
+    ) {
+        assert!(matches!(self.tcx().def_kind(trait_def_id), DefKind::Trait | DefKind::TraitAlias));
+        if self.requires_default_supertraits(hir_bounds, hir_generics) {
+            let self_ty_where_predicates = (trait_def_id, hir_generics.predicates);
+            self.add_default_traits_with_filter(
+                bounds,
+                self.tcx().types.self_param,
+                hir_bounds,
+                Some(self_ty_where_predicates),
+                span,
+                |default_trait| default_trait != hir::LangItem::Sized,
+            );
+        }
+    }
+
+    pub(crate) fn add_default_traits_with_filter(
+        &self,
+        bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
+        self_ty: Ty<'tcx>,
+        hir_bounds: &[hir::GenericBound<'tcx>],
+        self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+        span: Span,
+        f: impl Fn(hir::LangItem) -> bool,
+    ) {
+        self.tcx().default_traits().iter().filter(|&&default_trait| f(default_trait)).for_each(
+            |default_trait| {
+                self.add_default_trait(
+                    *default_trait,
+                    bounds,
+                    self_ty,
+                    hir_bounds,
+                    self_ty_where_predicates,
+                    span,
+                );
+            },
+        );
+    }
+
+    /// Add a `Sized` or `experimental_default_bounds` bounds to the `bounds` if appropriate.
+    ///
+    /// Doesn't add the bound if the HIR bounds contain any of `Trait`, `?Trait` or `!Trait`.
+    pub(crate) fn add_default_trait(
+        &self,
+        trait_: hir::LangItem,
+        bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
+        self_ty: Ty<'tcx>,
+        hir_bounds: &[hir::GenericBound<'tcx>],
         self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
         span: Span,
     ) {
+        let trait_id = self.tcx().lang_items().get(trait_);
+        if let Some(trait_id) = trait_id
+            && self.do_not_provide_default_trait_bound(
+                trait_id,
+                hir_bounds,
+                self_ty_where_predicates,
+            )
+        {
+            // There was no `?Trait` or `!Trait` bound;
+            // add `Trait` if it's available.
+            let trait_ref = ty::TraitRef::new(self.tcx(), trait_id, [self_ty]);
+            // Preferable to put this obligation first, since we report better errors for sized ambiguity.
+            bounds.insert(0, (trait_ref.upcast(self.tcx()), span));
+        }
+    }
+
+    fn do_not_provide_default_trait_bound<'a>(
+        &self,
+        trait_def_id: DefId,
+        hir_bounds: &'a [hir::GenericBound<'tcx>],
+        self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+    ) -> bool {
         let tcx = self.tcx();
-        let sized_def_id = tcx.lang_items().sized_trait();
-        let mut seen_negative_sized_bound = false;
-        let mut seen_positive_sized_bound = false;
+        let mut seen_negative_bound = false;
+        let mut seen_positive_bound = false;
 
         // Try to find an unbound in bounds.
         let mut unbounds: SmallVec<[_; 1]> = SmallVec::new();
-        let mut search_bounds = |hir_bounds: &'tcx [hir::GenericBound<'tcx>]| {
+        let mut search_bounds = |hir_bounds: &'a [hir::GenericBound<'tcx>]| {
             for hir_bound in hir_bounds {
                 let hir::GenericBound::Trait(ptr) = hir_bound else {
                     continue;
@@ -50,17 +215,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 match ptr.modifiers.polarity {
                     hir::BoundPolarity::Maybe(_) => unbounds.push(ptr),
                     hir::BoundPolarity::Negative(_) => {
-                        if let Some(sized_def_id) = sized_def_id
-                            && ptr.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id)
-                        {
-                            seen_negative_sized_bound = true;
+                        if ptr.trait_ref.path.res == Res::Def(DefKind::Trait, trait_def_id) {
+                            seen_negative_bound = true;
                         }
                     }
                     hir::BoundPolarity::Positive => {
-                        if let Some(sized_def_id) = sized_def_id
-                            && ptr.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id)
-                        {
-                            seen_positive_sized_bound = true;
+                        if ptr.trait_ref.path.res == Res::Def(DefKind::Trait, trait_def_id) {
+                            seen_positive_bound = true;
                         }
                     }
                 }
@@ -95,32 +256,36 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             };
         }
 
-        let mut seen_sized_unbound = false;
+        let mut seen_unbound = false;
         for unbound in unbounds {
-            if let Some(sized_def_id) = sized_def_id
-                && unbound.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id)
-            {
-                seen_sized_unbound = true;
-                continue;
+            let unbound_def_id = unbound.trait_ref.trait_def_id();
+            if unbound_def_id == Some(trait_def_id) {
+                seen_unbound = true;
+            }
+            let emit_relax_err = || {
+                let unbound_traits =
+                    match self.tcx().sess.opts.unstable_opts.experimental_default_bounds {
+                        true => "`?Sized` and `experimental_default_bounds`",
+                        false => "`?Sized`",
+                    };
+                // There was a `?Trait` bound, but it was neither `?Sized` nor `experimental_default_bounds`.
+                tcx.dcx().span_err(
+                    unbound.span,
+                    format!(
+                        "relaxing a default bound only does something for {}; \
+                    all other traits are not bound by default",
+                        unbound_traits
+                    ),
+                );
+            };
+            match unbound_def_id {
+                Some(def_id) if !tcx.is_default_trait(def_id) => emit_relax_err(),
+                None => emit_relax_err(),
+                _ => {}
             }
-            // There was a `?Trait` bound, but it was not `?Sized`
-            self.dcx().span_err(
-                unbound.span,
-                "relaxing a default bound only does something for `?Sized`; \
-                all other traits are not bound by default",
-            );
         }
 
-        if seen_sized_unbound || seen_negative_sized_bound || seen_positive_sized_bound {
-            // There was in fact a `?Sized`, `!Sized` or explicit `Sized` bound;
-            // we don't need to do anything.
-        } else if let Some(sized_def_id) = sized_def_id {
-            // There was no `?Sized`, `!Sized` or explicit `Sized` bound;
-            // add `Sized` if it's available.
-            let trait_ref = ty::TraitRef::new(tcx, sized_def_id, [self_ty]);
-            // Preferable to put this obligation first, since we report better errors for sized ambiguity.
-            bounds.insert(0, (trait_ref.upcast(tcx), span));
-        }
+        !(seen_unbound || seen_negative_bound || seen_positive_bound)
     }
 
     /// Lower HIR bounds into `bounds` given the self type `param_ty` and the overarching late-bound vars if any.
@@ -684,7 +849,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             return Err(self.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit {
                 span: path_span,
                 ty: tcx.liberate_late_bound_regions(item_def_id, output),
-                fn_span: tcx.hir().span_if_local(item_def_id),
+                fn_span: tcx.hir_span_if_local(item_def_id),
                 note: (),
             }));
         };
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
index 170500c7a16..ecb453bced0 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
@@ -32,7 +32,7 @@ pub(crate) fn validate_cmse_abi<'tcx>(
                         span,
                         ..
                     }) => *span,
-                    _ => tcx.hir().span(hir_id),
+                    _ => tcx.hir_span(hir_id),
                 };
                 struct_span_code_err!(
                     tcx.dcx(),
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
index 27643e715e6..aeebe45f881 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
@@ -57,6 +57,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             }
         }
 
+        let ast_bounds: Vec<_> =
+            hir_bounds.iter().map(|&trait_ref| hir::GenericBound::Trait(trait_ref)).collect();
+
+        self.add_default_traits_with_filter(
+            &mut user_written_bounds,
+            dummy_self,
+            &ast_bounds,
+            None,
+            span,
+            |tr| tr != hir::LangItem::Sized,
+        );
+
         let (elaborated_trait_bounds, elaborated_projection_bounds) =
             traits::expand_trait_aliases(tcx, user_written_bounds.iter().copied());
         let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = elaborated_trait_bounds
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index 3b007c77198..6e9c178d33a 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -882,7 +882,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
             let rename_message = if is_shadowed { ", consider renaming it" } else { "" };
 
-            if let Some(sp) = tcx.hir().span_if_local(item.def_id) {
+            if let Some(sp) = tcx.hir_span_if_local(item.def_id) {
                 err.span_label(
                     sp,
                     format!("`{}{}` defined here{}", prefix, item.name, rename_message),
@@ -1381,7 +1381,7 @@ pub(crate) fn fn_trait_to_string(
         .find_map(|c| {
             if c.ident.name == sym::Output
                 && let Some(ty) = c.ty()
-                && ty.span != tcx.hir().span(trait_segment.hir_id)
+                && ty.span != tcx.hir_span(trait_segment.hir_id)
             {
                 tcx.sess.source_map().span_to_snippet(ty.span).ok()
             } else {
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
index 60a60f6415a..21f0f9648ea 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
@@ -92,7 +92,7 @@ fn generic_arg_mismatch_err(
             GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }),
             GenericParamDefKind::Const { .. },
         ) if tcx.type_of(param.def_id).skip_binder() == tcx.types.usize => {
-            let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id));
+            let snippet = sess.source_map().span_to_snippet(tcx.hir_span(len.hir_id));
             if let Ok(snippet) = snippet {
                 err.span_suggestion(
                     arg.span(),
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
index 55886312284..8e62dce2191 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
@@ -86,6 +86,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 "expected a type, found a trait"
             );
             if self_ty.span.can_be_used_for_suggestions()
+                && poly_trait_ref.trait_ref.trait_def_id().is_some()
                 && !self.maybe_suggest_impl_trait(self_ty, &mut diag)
                 && !self.maybe_suggest_dyn_trait(self_ty, sugg, &mut diag)
             {
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 76a880da418..83925b5ec52 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -1066,7 +1066,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 let bound_span = tcx
                     .associated_items(bound_id)
                     .find_by_name_and_kind(tcx, assoc_name, assoc_kind, bound_id)
-                    .and_then(|item| tcx.hir().span_if_local(item.def_id));
+                    .and_then(|item| tcx.hir_span_if_local(item.def_id));
 
                 if let Some(bound_span) = bound_span {
                     err.span_label(
@@ -1400,7 +1400,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         );
                     }
 
-                    if let Some(sp) = tcx.hir().span_if_local(adt_def.did()) {
+                    if let Some(sp) = tcx.hir_span_if_local(adt_def.did()) {
                         err.span_label(sp, format!("variant `{assoc_ident}` not found here"));
                     }
 
@@ -1730,25 +1730,23 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         .is_accessible_from(self.item_def_id(), tcx)
                     && tcx.all_impls(*trait_def_id)
                         .any(|impl_def_id| {
-                            let impl_header = tcx.impl_trait_header(impl_def_id);
-                            impl_header.is_some_and(|header| {
-                                let trait_ref = header.trait_ref.instantiate(
-                                    tcx,
-                                    infcx.fresh_args_for_item(DUMMY_SP, impl_def_id),
-                                );
-
-                                let value = fold_regions(tcx, qself_ty, |_, _| tcx.lifetimes.re_erased);
-                                // FIXME: Don't bother dealing with non-lifetime binders here...
-                                if value.has_escaping_bound_vars() {
-                                    return false;
-                                }
-                                infcx
-                                    .can_eq(
-                                        ty::ParamEnv::empty(),
-                                        trait_ref.self_ty(),
-                                        value,
-                                    ) && header.polarity != ty::ImplPolarity::Negative
-                            })
+                            let header = tcx.impl_trait_header(impl_def_id).unwrap();
+                            let trait_ref = header.trait_ref.instantiate(
+                                tcx,
+                                infcx.fresh_args_for_item(DUMMY_SP, impl_def_id),
+                            );
+
+                            let value = fold_regions(tcx, qself_ty, |_, _| tcx.lifetimes.re_erased);
+                            // FIXME: Don't bother dealing with non-lifetime binders here...
+                            if value.has_escaping_bound_vars() {
+                                return false;
+                            }
+                            infcx
+                                .can_eq(
+                                    ty::ParamEnv::empty(),
+                                    trait_ref.self_ty(),
+                                    value,
+                                ) && header.polarity != ty::ImplPolarity::Negative
                         })
             })
             .map(|trait_def_id| tcx.def_path_str(trait_def_id))
diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
index e27a81d4976..c01b81563dc 100644
--- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
@@ -170,7 +170,7 @@ fn diagnostic_hir_wf_check<'tcx>(
                 ..
             }) => vec![*ty],
             hir::Node::AnonConst(_) => {
-                if let Some(const_param_id) = tcx.hir().opt_const_param_default_param_def_id(hir_id)
+                if let Some(const_param_id) = tcx.hir_opt_const_param_default_param_def_id(hir_id)
                     && let hir::Node::GenericParam(hir::GenericParam {
                         kind: hir::GenericParamKind::Const { ty, .. },
                         ..
diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs
index 32b05dcc569..daa908c8c78 100644
--- a/compiler/rustc_hir_analysis/src/outlives/mod.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs
@@ -25,7 +25,7 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[(ty::Clau
         }
         DefKind::AnonConst if tcx.features().generic_const_exprs() => {
             let id = tcx.local_def_id_to_hir_id(item_def_id);
-            if tcx.hir().opt_const_param_default_param_def_id(id).is_some() {
+            if tcx.hir_opt_const_param_default_param_def_id(id).is_some() {
                 // In `generics_of` we set the generics' parent to be our parent's parent which means that
                 // we lose out on the predicates of our actual parent if we dont return those predicates here.
                 // (See comment in `generics_of` for more information on why the parent shenanigans is necessary)
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 1c23761b2e5..8c0c17f7a7d 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -1271,18 +1271,18 @@ impl<'a> State<'a> {
         self.print_call_post(base_args)
     }
 
-    fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) {
-        let binop_prec = op.node.precedence();
+    fn print_expr_binary(&mut self, op: hir::BinOpKind, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) {
+        let binop_prec = op.precedence();
         let left_prec = lhs.precedence();
         let right_prec = rhs.precedence();
 
-        let (mut left_needs_paren, right_needs_paren) = match op.node.fixity() {
+        let (mut left_needs_paren, right_needs_paren) = match op.fixity() {
             Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
             Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec),
             Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec),
         };
 
-        match (&lhs.kind, op.node) {
+        match (&lhs.kind, op) {
             // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is
             // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead
             // of `(x as i32) < ...`. We need to convince it _not_ to do that.
@@ -1297,7 +1297,7 @@ impl<'a> State<'a> {
 
         self.print_expr_cond_paren(lhs, left_needs_paren);
         self.space();
-        self.word_space(op.node.as_str());
+        self.word_space(op.as_str());
         self.print_expr_cond_paren(rhs, right_needs_paren);
     }
 
@@ -1451,7 +1451,7 @@ impl<'a> State<'a> {
                 self.word(".use");
             }
             hir::ExprKind::Binary(op, lhs, rhs) => {
-                self.print_expr_binary(op, lhs, rhs);
+                self.print_expr_binary(op.node, lhs, rhs);
             }
             hir::ExprKind::Unary(op, expr) => {
                 self.print_expr_unary(op, expr);
@@ -1572,8 +1572,7 @@ impl<'a> State<'a> {
             hir::ExprKind::AssignOp(op, lhs, rhs) => {
                 self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign);
                 self.space();
-                self.word(op.node.as_str());
-                self.word_space("=");
+                self.word_space(op.node.as_str());
                 self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign);
             }
             hir::ExprKind::Field(expr, ident) => {
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 5e2daa69628..02fd7367e2f 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -771,7 +771,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     format!("this {descr} returns an unsized value `{output_ty}`, so it cannot be called")
                 );
                 if let DefIdOrName::DefId(def_id) = maybe_def
-                    && let Some(def_span) = self.tcx.hir().span_if_local(def_id)
+                    && let Some(def_span) = self.tcx.hir_span_if_local(def_id)
                 {
                     err.span_label(def_span, "the callable type is defined here");
                 }
@@ -780,7 +780,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         }
 
-        if let Some(span) = self.tcx.hir().res_span(def) {
+        if let Some(span) = self.tcx.hir_res_span(def) {
             let callee_ty = callee_ty.to_string();
             let label = match (unit_variant, inner_callee_path) {
                 (Some((_, kind, path)), _) => {
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index a4776338f6c..467ca26e7ea 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -970,7 +970,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result);
 
         // Normalize only after registering in `user_provided_sigs`.
-        self.normalize(self.tcx.hir().span(hir_id), result)
+        self.normalize(self.tcx.hir_span(hir_id), result)
     }
 
     /// Invoked when we are translating the coroutine that results
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 41bdd0ca43e..b845e2190ef 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -999,10 +999,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let container = with_no_trimmed_paths!(self.tcx.def_path_str(container_id));
         for def_id in pick.import_ids {
             let hir_id = self.tcx.local_def_id_to_hir_id(def_id);
-            path_span.push_span_label(
-                self.tcx.hir().span(hir_id),
-                format!("`{container}` imported here"),
-            );
+            path_span
+                .push_span_label(self.tcx.hir_span(hir_id), format!("`{container}` imported here"));
         }
         let tail = with_no_trimmed_paths!(match &other_methods_in_scope[..] {
             [] => return,
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index a75f6f4caac..45ab8e03db5 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -512,7 +512,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.check_expr_assign(expr, expected, lhs, rhs, span)
             }
             ExprKind::AssignOp(op, lhs, rhs) => {
-                self.check_expr_binop_assign(expr, op, lhs, rhs, expected)
+                self.check_expr_assign_op(expr, op, lhs, rhs, expected)
             }
             ExprKind::Unary(unop, oprnd) => self.check_expr_unop(unop, oprnd, expected, expr),
             ExprKind::AddrOf(kind, mutbl, oprnd) => {
@@ -3239,7 +3239,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Some(x) => self.tcx.local_def_id_to_hir_id(x),
             None => return,
         };
-        let param_span = self.tcx.hir().span(param_hir_id);
+        let param_span = self.tcx.hir_span(param_hir_id);
         let param_name = self.tcx.hir_ty_param_name(param_def_id.expect_local());
 
         err.span_label(param_span, format!("type parameter '{param_name}' declared here"));
diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
index 6fb289235de..64176dacb73 100644
--- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
@@ -1182,9 +1182,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
                 self.cx.error_reported_in_ty(ty)?;
                 if ty.is_ty_var() {
                     debug!("resolve_type_vars_or_bug: infer var from {:?}", ty);
-                    Err(self
-                        .cx
-                        .report_bug(self.cx.tcx().hir().span(id), "encountered type variable"))
+                    Err(self.cx.report_bug(self.cx.tcx().hir_span(id), "encountered type variable"))
                 } else {
                     Ok(ty)
                 }
@@ -1509,10 +1507,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
         if node_ty != place_ty
             && self
                 .cx
-                .try_structurally_resolve_type(
-                    self.cx.tcx().hir().span(base_place.hir_id),
-                    place_ty,
-                )
+                .try_structurally_resolve_type(self.cx.tcx().hir_span(base_place.hir_id), place_ty)
                 .is_impl_trait()
         {
             projections.push(Projection { kind: ProjectionKind::OpaqueCast, ty: node_ty });
@@ -1551,17 +1546,14 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
         let base_curr_ty = base_place.place.ty();
         let deref_ty = match self
             .cx
-            .try_structurally_resolve_type(
-                self.cx.tcx().hir().span(base_place.hir_id),
-                base_curr_ty,
-            )
+            .try_structurally_resolve_type(self.cx.tcx().hir_span(base_place.hir_id), base_curr_ty)
             .builtin_deref(true)
         {
             Some(ty) => ty,
             None => {
                 debug!("explicit deref of non-derefable type: {:?}", base_curr_ty);
                 return Err(self.cx.report_bug(
-                    self.cx.tcx().hir().span(node),
+                    self.cx.tcx().hir_span(node),
                     "explicit deref of non-derefable type",
                 ));
             }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index d75c2853ba0..91190a32ff5 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -157,7 +157,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Lots of that diagnostics code relies on subtle effects of re-lowering, so we'll
                 // let it keep doing that and just ensure that compilation won't succeed.
                 self.dcx().span_delayed_bug(
-                    self.tcx.hir().span(id),
+                    self.tcx.hir_span(id),
                     format!("`{prev}` overridden by `{ty}` for {id:?} in {:?}", self.body_id),
                 );
             }
@@ -532,7 +532,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let ct = self.lowerer().lower_const_arg(const_arg, feed);
         self.register_wf_obligation(
             ct.into(),
-            self.tcx.hir().span(const_arg.hir_id),
+            self.tcx.hir_span(const_arg.hir_id),
             ObligationCauseCode::WellFormed(None),
         );
         ct
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 264719ca569..912098c4e2d 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -351,7 +351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         if self.suggest_fn_call(err, expr, found, |output| self.may_coerce(output, expected))
             && let ty::FnDef(def_id, ..) = *found.kind()
-            && let Some(sp) = self.tcx.hir().span_if_local(def_id)
+            && let Some(sp) = self.tcx.hir_span_if_local(def_id)
         {
             let name = self.tcx.item_name(def_id);
             let kind = self.tcx.def_kind(def_id);
@@ -407,7 +407,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))]
                     };
                     let struct_pat_shorthand_field =
-                        self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr);
+                        self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr);
                     if let Some(name) = struct_pat_shorthand_field {
                         sugg.insert(0, (expr.span.shrink_to_lo(), format!("{name}: ")));
                     }
@@ -449,7 +449,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             });
 
             let prefix_wrap = |sugg: &str| {
-                if let Some(name) = self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+                if let Some(name) = self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
                     format!(": {}{}", name, sugg)
                 } else {
                     sugg.to_string()
@@ -671,7 +671,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         }
                         _ => {
                             let prefix = if let Some(name) =
-                                self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr)
+                                self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr)
                             {
                                 format!("{}: ", name)
                             } else {
@@ -1153,7 +1153,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
         if can_return
             && let Some(span) = expr.span.find_ancestor_inside(
-                self.tcx.hir().span_with_body(self.tcx.local_def_id_to_hir_id(fn_id)),
+                self.tcx.hir_span_with_body(self.tcx.local_def_id_to_hir_id(fn_id)),
             )
         {
             // When the expr is in a match arm's body, we shouldn't add semicolon ';' at the end.
@@ -1275,7 +1275,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 )
                 .must_apply_modulo_regions()
         {
-            let suggestion = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+            let suggestion = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
                 Some(ident) => format!(": {ident}.clone()"),
                 None => ".clone()".to_string(),
             };
@@ -1381,7 +1381,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     (span.shrink_to_hi(), ").into()".to_owned()),
                 ]
             };
-            if let Some(name) = self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+            if let Some(name) = self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
                 sugg.insert(0, (expr.span.shrink_to_lo(), format!("{}: ", name)));
             }
             diag.multipart_suggestion(
@@ -1436,7 +1436,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return false;
         }
 
-        let suggestion = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+        let suggestion = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
             Some(ident) => format!(": {ident}.is_some()"),
             None => ".is_some()".to_string(),
         };
@@ -2032,7 +2032,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             )
         };
 
-        let sugg = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+        let sugg = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
             Some(ident) => format!(": {ident}{sugg}"),
             None => sugg.to_string(),
         };
@@ -2289,7 +2289,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Unroll desugaring, to make sure this works for `for` loops etc.
                 loop {
                     parent = self.tcx.parent_hir_id(id);
-                    let parent_span = self.tcx.hir().span(parent);
+                    let parent_span = self.tcx.hir_span(parent);
                     if parent_span.find_ancestor_inside(expr.span).is_some() {
                         // The parent node is part of the same span, so is the result of the
                         // same expansion/desugaring and not the 'real' parent node.
@@ -2378,7 +2378,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 .collect();
 
             let suggestions_for = |variant: &_, ctor_kind, field_name| {
-                let prefix = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+                let prefix = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
                     Some(ident) => format!("{ident}: "),
                     None => String::new(),
                 };
@@ -2700,8 +2700,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         ));
                     }
 
-                    let prefix = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr)
-                    {
+                    let prefix = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
                         Some(ident) => format!("{ident}: "),
                         None => String::new(),
                     };
@@ -2911,7 +2910,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         };
 
                         let prefix =
-                            match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+                            match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
                                 Some(ident) => format!("{ident}: "),
                                 None => String::new(),
                             };
@@ -3478,30 +3477,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         lhs_ty: Ty<'tcx>,
         rhs_expr: &'tcx hir::Expr<'tcx>,
         lhs_expr: &'tcx hir::Expr<'tcx>,
-        op: hir::BinOp,
     ) {
-        match op.node {
-            hir::BinOpKind::Eq => {
-                if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait()
-                    && self
-                        .infcx
-                        .type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env)
-                        .must_apply_modulo_regions()
-                {
-                    let sm = self.tcx.sess.source_map();
-                    if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span)
-                        && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span)
-                    {
-                        err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`"));
-                        err.multipart_suggestion(
-                            "consider swapping the equality",
-                            vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)],
-                            Applicability::MaybeIncorrect,
-                        );
-                    }
-                }
+        if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait()
+            && self
+                .infcx
+                .type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env)
+                .must_apply_modulo_regions()
+        {
+            let sm = self.tcx.sess.source_map();
+            if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span)
+                && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span)
+            {
+                err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`"));
+                err.multipart_suggestion(
+                    "consider swapping the equality",
+                    vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)],
+                    Applicability::MaybeIncorrect,
+                );
             }
-            _ => {}
         }
     }
 }
diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs
index 54e9e699353..194e420b606 100644
--- a/compiler/rustc_hir_typeck/src/intrinsicck.rs
+++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs
@@ -45,7 +45,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub(crate) fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) {
         let tcx = self.tcx;
         let dl = &tcx.data_layout;
-        let span = tcx.hir().span(hir_id);
+        let span = tcx.hir_span(hir_id);
         let normalize = |ty| {
             let ty = self.resolve_vars_if_possible(ty);
             if let Ok(ty) =
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 4968998fd51..46389668de7 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -117,7 +117,7 @@ fn typeck_with_inspect<'tcx>(
 
     let id = tcx.local_def_id_to_hir_id(def_id);
     let node = tcx.hir_node(id);
-    let span = tcx.hir().span(id);
+    let span = tcx.hir_span(id);
 
     // Figure out what primary body this item has.
     let body_id = node.body_id().unwrap_or_else(|| {
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 908c3ee2eb8..246b23f11b6 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -254,11 +254,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     item_name
                 );
                 err.span_label(item_name.span, format!("private {kind}"));
-                let sp = self
-                    .tcx
-                    .hir()
-                    .span_if_local(def_id)
-                    .unwrap_or_else(|| self.tcx.def_span(def_id));
+                let sp =
+                    self.tcx.hir_span_if_local(def_id).unwrap_or_else(|| self.tcx.def_span(def_id));
                 err.span_label(sp, format!("private {kind} defined here"));
                 if let Some(within_macro_span) = within_macro_span {
                     err.span_label(within_macro_span, "due to this macro variable");
@@ -566,7 +563,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     span.push_span_label(sugg_let.span,
                             format!("`{rcvr_name}` of type `{self_ty}` that has method `{method_name}` defined earlier here"));
                     span.push_span_label(
-                        self.tcx.hir().span(recv_id),
+                        self.tcx.hir_span(recv_id),
                         format!(
                             "earlier `{rcvr_name}` shadowed here with type `{ty_str_reported}`"
                         ),
@@ -2561,7 +2558,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 ExprKind::Path(QPath::Resolved(_, path)) => {
                     // local binding
                     if let hir::def::Res::Local(hir_id) = path.res {
-                        let span = tcx.hir().span(hir_id);
+                        let span = tcx.hir_span(hir_id);
                         let filename = tcx.sess.source_map().span_to_filename(span);
 
                         let parent_node = self.tcx.parent_hir_node(hir_id);
diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs
index a473e14b244..93f77b8409f 100644
--- a/compiler/rustc_hir_typeck/src/op.rs
+++ b/compiler/rustc_hir_typeck/src/op.rs
@@ -4,15 +4,15 @@ use rustc_data_structures::packed::Pu128;
 use rustc_errors::codes::*;
 use rustc_errors::{Applicability, Diag, struct_span_code_err};
 use rustc_infer::traits::ObligationCauseCode;
+use rustc_middle::bug;
 use rustc_middle::ty::adjustment::{
     Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
 };
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
-use rustc_middle::{bug, span_bug};
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::source_map::Spanned;
-use rustc_span::{Ident, Span, sym};
+use rustc_span::{Ident, Span, Symbol, sym};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::{FulfillmentError, Obligation, ObligationCtxt};
 use tracing::debug;
@@ -24,24 +24,27 @@ use crate::Expectation;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Checks a `a <op>= b`
-    pub(crate) fn check_expr_binop_assign(
+    pub(crate) fn check_expr_assign_op(
         &self,
         expr: &'tcx hir::Expr<'tcx>,
-        op: hir::BinOp,
+        op: hir::AssignOp,
         lhs: &'tcx hir::Expr<'tcx>,
         rhs: &'tcx hir::Expr<'tcx>,
         expected: Expectation<'tcx>,
     ) -> Ty<'tcx> {
         let (lhs_ty, rhs_ty, return_ty) =
-            self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes, expected);
-
-        let ty =
-            if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
-                self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op);
-                self.tcx.types.unit
-            } else {
-                return_ty
-            };
+            self.check_overloaded_binop(expr, lhs, rhs, Op::AssignOp(op), expected);
+
+        let category = BinOpCategory::from(op.node);
+        let ty = if !lhs_ty.is_ty_var()
+            && !rhs_ty.is_ty_var()
+            && is_builtin_binop(lhs_ty, rhs_ty, category)
+        {
+            self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, category);
+            self.tcx.types.unit
+        } else {
+            return_ty
+        };
 
         self.check_lhs_assignable(lhs, E0067, op.span, |err| {
             if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
@@ -49,7 +52,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .lookup_op_method(
                         (lhs, lhs_deref_ty),
                         Some((rhs, rhs_ty)),
-                        Op::Binary(op, IsAssign::Yes),
+                        lang_item_for_binop(self.tcx, Op::AssignOp(op)),
+                        op.span,
                         expected,
                     )
                     .is_ok()
@@ -60,7 +64,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         .lookup_op_method(
                             (lhs, lhs_ty),
                             Some((rhs, rhs_ty)),
-                            Op::Binary(op, IsAssign::Yes),
+                            lang_item_for_binop(self.tcx, Op::AssignOp(op)),
+                            op.span,
                             expected,
                         )
                         .is_err()
@@ -98,7 +103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             expr.hir_id, expr, op, lhs_expr, rhs_expr
         );
 
-        match BinOpCategory::from(op) {
+        match BinOpCategory::from(op.node) {
             BinOpCategory::Shortcircuit => {
                 // && and || are a simple case.
                 self.check_expr_coercible_to_type(lhs_expr, tcx.types.bool, None);
@@ -114,14 +119,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Otherwise, we always treat operators as if they are
                 // overloaded. This is the way to be most flexible w/r/t
                 // types that get inferred.
-                let (lhs_ty, rhs_ty, return_ty) = self.check_overloaded_binop(
-                    expr,
-                    lhs_expr,
-                    rhs_expr,
-                    op,
-                    IsAssign::No,
-                    expected,
-                );
+                let (lhs_ty, rhs_ty, return_ty) =
+                    self.check_overloaded_binop(expr, lhs_expr, rhs_expr, Op::BinOp(op), expected);
 
                 // Supply type inference hints if relevant. Probably these
                 // hints should be enforced during select as part of the
@@ -135,16 +134,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // deduce that the result type should be `u32`, even
                 // though we don't know yet what type 2 has and hence
                 // can't pin this down to a specific impl.
+                let category = BinOpCategory::from(op.node);
                 if !lhs_ty.is_ty_var()
                     && !rhs_ty.is_ty_var()
-                    && is_builtin_binop(lhs_ty, rhs_ty, op)
+                    && is_builtin_binop(lhs_ty, rhs_ty, category)
                 {
                     let builtin_return_ty = self.enforce_builtin_binop_types(
                         lhs_expr.span,
                         lhs_ty,
                         rhs_expr.span,
                         rhs_ty,
-                        op,
+                        category,
                     );
                     self.demand_eqtype(expr.span, builtin_return_ty, return_ty);
                     builtin_return_ty
@@ -161,16 +161,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         lhs_ty: Ty<'tcx>,
         rhs_span: Span,
         rhs_ty: Ty<'tcx>,
-        op: hir::BinOp,
+        category: BinOpCategory,
     ) -> Ty<'tcx> {
-        debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
+        debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, category));
 
         // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
         // (See https://github.com/rust-lang/rust/issues/57447.)
         let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty));
 
         let tcx = self.tcx;
-        match BinOpCategory::from(op) {
+        match category {
             BinOpCategory::Shortcircuit => {
                 self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty);
                 self.demand_suptype(rhs_span, tcx.types.bool, rhs_ty);
@@ -201,17 +201,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr: &'tcx hir::Expr<'tcx>,
         lhs_expr: &'tcx hir::Expr<'tcx>,
         rhs_expr: &'tcx hir::Expr<'tcx>,
-        op: hir::BinOp,
-        is_assign: IsAssign,
+        op: Op,
         expected: Expectation<'tcx>,
     ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
-        debug!(
-            "check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})",
-            expr.hir_id, op, is_assign
-        );
+        debug!("check_overloaded_binop(expr.hir_id={}, op={:?})", expr.hir_id, op);
 
-        let lhs_ty = match is_assign {
-            IsAssign::No => {
+        let lhs_ty = match op {
+            Op::BinOp(_) => {
                 // Find a suitable supertype of the LHS expression's type, by coercing to
                 // a type variable, to pass as the `Self` to the trait, avoiding invariant
                 // trait matching creating lifetime constraints that are too strict.
@@ -221,7 +217,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let fresh_var = self.next_ty_var(lhs_expr.span);
                 self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No)
             }
-            IsAssign::Yes => {
+            Op::AssignOp(_) => {
                 // rust-lang/rust#52126: We have to use strict
                 // equivalence on the LHS of an assign-op like `+=`;
                 // overwritten or mutably-borrowed places cannot be
@@ -242,7 +238,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let result = self.lookup_op_method(
             (lhs_expr, lhs_ty),
             Some((rhs_expr, rhs_ty_var)),
-            Op::Binary(op, is_assign),
+            lang_item_for_binop(self.tcx, op),
+            op.span(),
             expected,
         );
 
@@ -252,15 +249,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             rhs_ty_var,
             Some(lhs_expr),
             |err, ty| {
-                self.suggest_swapping_lhs_and_rhs(err, ty, lhs_ty, rhs_expr, lhs_expr, op);
+                if let Op::BinOp(binop) = op
+                    && binop.node == hir::BinOpKind::Eq
+                {
+                    self.suggest_swapping_lhs_and_rhs(err, ty, lhs_ty, rhs_expr, lhs_expr);
+                }
             },
         );
         let rhs_ty = self.resolve_vars_with_obligations(rhs_ty);
 
         let return_ty = match result {
             Ok(method) => {
-                let by_ref_binop = !op.node.is_by_value();
-                if is_assign == IsAssign::Yes || by_ref_binop {
+                let by_ref_binop = !op.is_by_value();
+                if matches!(op, Op::AssignOp(_)) || by_ref_binop {
                     if let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() {
                         let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes);
                         let autoref = Adjustment {
@@ -301,32 +302,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 Ty::new_misc_error(self.tcx)
             }
             Err(errors) => {
-                let (_, trait_def_id) =
-                    lang_item_for_op(self.tcx, Op::Binary(op, is_assign), op.span);
+                let (_, trait_def_id) = lang_item_for_binop(self.tcx, op);
                 let missing_trait = trait_def_id
                     .map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id)));
                 let mut path = None;
                 let lhs_ty_str = self.tcx.short_string(lhs_ty, &mut path);
                 let rhs_ty_str = self.tcx.short_string(rhs_ty, &mut path);
-                let (mut err, output_def_id) = match is_assign {
-                    IsAssign::Yes => {
+                let (mut err, output_def_id) = match op {
+                    Op::AssignOp(assign_op) => {
+                        let s = assign_op.node.as_str();
                         let mut err = struct_span_code_err!(
                             self.dcx(),
                             expr.span,
                             E0368,
-                            "binary assignment operation `{}=` cannot be applied to type `{}`",
-                            op.node.as_str(),
+                            "binary assignment operation `{}` cannot be applied to type `{}`",
+                            s,
                             lhs_ty_str,
                         );
                         err.span_label(
                             lhs_expr.span,
-                            format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty_str),
+                            format!("cannot use `{}` on type `{}`", s, lhs_ty_str),
                         );
                         self.note_unmet_impls_on_type(&mut err, errors, false);
                         (err, None)
                     }
-                    IsAssign::No => {
-                        let message = match op.node {
+                    Op::BinOp(bin_op) => {
+                        let message = match bin_op.node {
                             hir::BinOpKind::Add => {
                                 format!("cannot add `{rhs_ty_str}` to `{lhs_ty_str}`")
                             }
@@ -362,8 +363,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             }
                             _ => format!(
                                 "binary operation `{}` cannot be applied to type `{}`",
-                                op.node.as_str(),
-                                lhs_ty_str,
+                                bin_op.node.as_str(),
+                                lhs_ty_str
                             ),
                         };
                         let output_def_id = trait_def_id.and_then(|def_id| {
@@ -376,7 +377,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 .cloned()
                         });
                         let mut err =
-                            struct_span_code_err!(self.dcx(), op.span, E0369, "{message}");
+                            struct_span_code_err!(self.dcx(), bin_op.span, E0369, "{message}");
                         if !lhs_expr.span.eq(&rhs_expr.span) {
                             err.span_label(lhs_expr.span, lhs_ty_str.clone());
                             err.span_label(rhs_expr.span, rhs_ty_str);
@@ -409,18 +410,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         .lookup_op_method(
                             (lhs_expr, lhs_deref_ty),
                             Some((rhs_expr, rhs_ty)),
-                            Op::Binary(op, is_assign),
+                            lang_item_for_binop(self.tcx, op),
+                            op.span(),
                             expected,
                         )
                         .is_ok()
                     {
                         let msg = format!(
-                            "`{}{}` can be used on `{}` if you dereference the left-hand side",
-                            op.node.as_str(),
-                            match is_assign {
-                                IsAssign::Yes => "=",
-                                IsAssign::No => "",
-                            },
+                            "`{}` can be used on `{}` if you dereference the left-hand side",
+                            op.as_str(),
                             self.tcx.short_string(lhs_deref_ty, err.long_ty_path()),
                         );
                         err.span_suggestion_verbose(
@@ -442,14 +440,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             .lookup_op_method(
                                 (lhs_expr, lhs_adjusted_ty),
                                 Some((rhs_expr, rhs_adjusted_ty)),
-                                Op::Binary(op, is_assign),
+                                lang_item_for_binop(self.tcx, op),
+                                op.span(),
                                 expected,
                             )
                             .is_ok()
                         {
                             let lhs = self.tcx.short_string(lhs_adjusted_ty, err.long_ty_path());
                             let rhs = self.tcx.short_string(rhs_adjusted_ty, err.long_ty_path());
-                            let op = op.node.as_str();
+                            let op = op.as_str();
                             err.note(format!("an implementation for `{lhs} {op} {rhs}` exists"));
 
                             if let Some(lhs_new_mutbl) = lhs_new_mutbl
@@ -499,7 +498,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     self.lookup_op_method(
                         (lhs_expr, lhs_ty),
                         Some((rhs_expr, rhs_ty)),
-                        Op::Binary(op, is_assign),
+                        lang_item_for_binop(self.tcx, op),
+                        op.span(),
                         expected,
                     )
                     .is_ok()
@@ -511,13 +511,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest
                 // `a += b` => `*a += b` if a is a mut ref.
-                if !op.span.can_be_used_for_suggestions() {
+                if !op.span().can_be_used_for_suggestions() {
                     // Suppress suggestions when lhs and rhs are not in the same span as the error
-                } else if is_assign == IsAssign::Yes
+                } else if let Op::AssignOp(_) = op
                     && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
                 {
                     suggest_deref_binop(&mut err, lhs_deref_ty);
-                } else if is_assign == IsAssign::No
+                } else if let Op::BinOp(_) = op
                     && let ty::Ref(region, lhs_deref_ty, mutbl) = lhs_ty.kind()
                 {
                     if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty) {
@@ -572,10 +572,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
 
                 if let Some(missing_trait) = missing_trait {
-                    if op.node == hir::BinOpKind::Add
-                        && self.check_str_addition(
-                            lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, op,
-                        )
+                    if matches!(
+                        op,
+                        Op::BinOp(Spanned { node: hir::BinOpKind::Add, .. })
+                            | Op::AssignOp(Spanned { node: hir::AssignOpKind::AddAssign, .. })
+                    ) && self
+                        .check_str_addition(lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, op)
                     {
                         // This has nothing here because it means we did string
                         // concatenation (e.g., "Hello " + "World!"). This means
@@ -592,7 +594,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             .lookup_op_method(
                                 (lhs_expr, lhs_ty),
                                 Some((rhs_expr, rhs_ty)),
-                                Op::Binary(op, is_assign),
+                                lang_item_for_binop(self.tcx, op),
+                                op.span(),
                                 expected,
                             )
                             .unwrap_err();
@@ -642,9 +645,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 // Suggest using `add`, `offset` or `offset_from` for pointer - {integer},
                 // pointer + {integer} or pointer - pointer.
-                if op.span.can_be_used_for_suggestions() {
-                    match op.node {
-                        hir::BinOpKind::Add if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() => {
+                if op.span().can_be_used_for_suggestions() {
+                    match op {
+                        Op::BinOp(Spanned { node: hir::BinOpKind::Add, .. })
+                            if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() =>
+                        {
                             err.multipart_suggestion(
                                 "consider using `wrapping_add` or `add` for pointer + {integer}",
                                 vec![
@@ -657,7 +662,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 Applicability::MaybeIncorrect,
                             );
                         }
-                        hir::BinOpKind::Sub => {
+                        Op::BinOp(Spanned { node: hir::BinOpKind::Sub, .. }) => {
                             if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() {
                                 err.multipart_suggestion(
                                     "consider using `wrapping_sub` or `sub` for \
@@ -713,8 +718,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         lhs_ty: Ty<'tcx>,
         rhs_ty: Ty<'tcx>,
         err: &mut Diag<'_>,
-        is_assign: IsAssign,
-        op: hir::BinOp,
+        op: Op,
     ) -> bool {
         let str_concat_note = "string concatenation requires an owned `String` on the left";
         let rm_borrow_msg = "remove the borrow to obtain an owned `String`";
@@ -733,8 +737,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             r_ty.kind(), ty::Ref(_, inner_ty, _) if *inner_ty.kind() == ty::Str
                         )) =>
             {
-                if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str`
-                    err.span_label(op.span, "`+` cannot be used to concatenate two `&str` strings");
+                if let Op::BinOp(_) = op { // Do not supply this message if `&str += &str`
+                    err.span_label(
+                        op.span(),
+                        "`+` cannot be used to concatenate two `&str` strings"
+                    );
                     err.note(str_concat_note);
                     if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
                         err.span_suggestion_verbose(
@@ -758,11 +765,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 if (*l_ty.kind() == ty::Str || is_std_string(l_ty)) && is_std_string(rhs_ty) =>
             {
                 err.span_label(
-                    op.span,
+                    op.span(),
                     "`+` cannot be used to concatenate a `&str` with a `String`",
                 );
-                match is_assign {
-                    IsAssign::No => {
+                match op {
+                    Op::BinOp(_) => {
                         let sugg_msg;
                         let lhs_sugg = if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
                             sugg_msg = "remove the borrow on the left and add one on the right";
@@ -781,7 +788,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             Applicability::MachineApplicable,
                         );
                     }
-                    IsAssign::Yes => {
+                    Op::AssignOp(_) => {
                         err.note(str_concat_note);
                     }
                 }
@@ -799,7 +806,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Expectation<'tcx>,
     ) -> Ty<'tcx> {
         assert!(op.is_by_value());
-        match self.lookup_op_method((ex, operand_ty), None, Op::Unary(op, ex.span), expected) {
+        match self.lookup_op_method(
+            (ex, operand_ty),
+            None,
+            lang_item_for_unop(self.tcx, op),
+            ex.span,
+            expected,
+        ) {
             Ok(method) => {
                 self.write_method_call_and_enforce_effects(ex.hir_id, ex.span, method);
                 method.sig.output()
@@ -898,21 +911,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         (lhs_expr, lhs_ty): (&'tcx hir::Expr<'tcx>, Ty<'tcx>),
         opt_rhs: Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)>,
-        op: Op,
+        (opname, trait_did): (Symbol, Option<hir::def_id::DefId>),
+        span: Span,
         expected: Expectation<'tcx>,
     ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
-        let span = match op {
-            Op::Binary(op, _) => op.span,
-            Op::Unary(_, span) => span,
-        };
-        let (opname, Some(trait_did)) = lang_item_for_op(self.tcx, op, span) else {
+        let Some(trait_did) = trait_did else {
             // Bail if the operator trait is not defined.
             return Err(vec![]);
         };
 
         debug!(
-            "lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})",
-            lhs_ty, op, opname, trait_did
+            "lookup_op_method(lhs_ty={:?}, opname={:?}, trait_did={:?})",
+            lhs_ty, opname, trait_did
         );
 
         let opname = Ident::with_dummy_span(opname);
@@ -980,37 +990,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 }
 
-fn lang_item_for_op(
-    tcx: TyCtxt<'_>,
-    op: Op,
-    span: Span,
-) -> (rustc_span::Symbol, Option<hir::def_id::DefId>) {
+fn lang_item_for_binop(tcx: TyCtxt<'_>, op: Op) -> (Symbol, Option<hir::def_id::DefId>) {
     let lang = tcx.lang_items();
-    if let Op::Binary(op, IsAssign::Yes) = op {
-        match op.node {
-            hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()),
-            hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()),
-            hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()),
-            hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()),
-            hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()),
-            hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()),
-            hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()),
-            hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()),
-            hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()),
-            hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()),
-            hir::BinOpKind::Lt
-            | hir::BinOpKind::Le
-            | hir::BinOpKind::Ge
-            | hir::BinOpKind::Gt
-            | hir::BinOpKind::Eq
-            | hir::BinOpKind::Ne
-            | hir::BinOpKind::And
-            | hir::BinOpKind::Or => {
-                span_bug!(span, "impossible assignment operation: {}=", op.node.as_str())
-            }
-        }
-    } else if let Op::Binary(op, IsAssign::No) = op {
-        match op.node {
+    match op {
+        Op::AssignOp(op) => match op.node {
+            hir::AssignOpKind::AddAssign => (sym::add_assign, lang.add_assign_trait()),
+            hir::AssignOpKind::SubAssign => (sym::sub_assign, lang.sub_assign_trait()),
+            hir::AssignOpKind::MulAssign => (sym::mul_assign, lang.mul_assign_trait()),
+            hir::AssignOpKind::DivAssign => (sym::div_assign, lang.div_assign_trait()),
+            hir::AssignOpKind::RemAssign => (sym::rem_assign, lang.rem_assign_trait()),
+            hir::AssignOpKind::BitXorAssign => (sym::bitxor_assign, lang.bitxor_assign_trait()),
+            hir::AssignOpKind::BitAndAssign => (sym::bitand_assign, lang.bitand_assign_trait()),
+            hir::AssignOpKind::BitOrAssign => (sym::bitor_assign, lang.bitor_assign_trait()),
+            hir::AssignOpKind::ShlAssign => (sym::shl_assign, lang.shl_assign_trait()),
+            hir::AssignOpKind::ShrAssign => (sym::shr_assign, lang.shr_assign_trait()),
+        },
+        Op::BinOp(op) => match op.node {
             hir::BinOpKind::Add => (sym::add, lang.add_trait()),
             hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()),
             hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()),
@@ -1028,20 +1023,24 @@ fn lang_item_for_op(
             hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()),
             hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()),
             hir::BinOpKind::And | hir::BinOpKind::Or => {
-                span_bug!(span, "&& and || are not overloadable")
+                bug!("&& and || are not overloadable")
             }
-        }
-    } else if let Op::Unary(hir::UnOp::Not, _) = op {
-        (sym::not, lang.not_trait())
-    } else if let Op::Unary(hir::UnOp::Neg, _) = op {
-        (sym::neg, lang.neg_trait())
-    } else {
-        bug!("lookup_op_method: op not supported: {:?}", op)
+        },
+    }
+}
+
+fn lang_item_for_unop(tcx: TyCtxt<'_>, op: hir::UnOp) -> (Symbol, Option<hir::def_id::DefId>) {
+    let lang = tcx.lang_items();
+    match op {
+        hir::UnOp::Not => (sym::not, lang.not_trait()),
+        hir::UnOp::Neg => (sym::neg, lang.neg_trait()),
+        hir::UnOp::Deref => bug!("Deref is not overloadable"),
     }
 }
 
 // Binary operator categories. These categories summarize the behavior
 // with respect to the builtin operations supported.
+#[derive(Clone, Copy)]
 enum BinOpCategory {
     /// &&, || -- cannot be overridden
     Shortcircuit,
@@ -1063,44 +1062,58 @@ enum BinOpCategory {
     Comparison,
 }
 
-impl BinOpCategory {
-    fn from(op: hir::BinOp) -> BinOpCategory {
-        match op.node {
-            hir::BinOpKind::Shl | hir::BinOpKind::Shr => BinOpCategory::Shift,
-
-            hir::BinOpKind::Add
-            | hir::BinOpKind::Sub
-            | hir::BinOpKind::Mul
-            | hir::BinOpKind::Div
-            | hir::BinOpKind::Rem => BinOpCategory::Math,
-
-            hir::BinOpKind::BitXor | hir::BinOpKind::BitAnd | hir::BinOpKind::BitOr => {
-                BinOpCategory::Bitwise
-            }
-
-            hir::BinOpKind::Eq
-            | hir::BinOpKind::Ne
-            | hir::BinOpKind::Lt
-            | hir::BinOpKind::Le
-            | hir::BinOpKind::Ge
-            | hir::BinOpKind::Gt => BinOpCategory::Comparison,
+impl From<hir::BinOpKind> for BinOpCategory {
+    fn from(op: hir::BinOpKind) -> BinOpCategory {
+        use hir::BinOpKind::*;
+        match op {
+            Shl | Shr => BinOpCategory::Shift,
+            Add | Sub | Mul | Div | Rem => BinOpCategory::Math,
+            BitXor | BitAnd | BitOr => BinOpCategory::Bitwise,
+            Eq | Ne | Lt | Le | Ge | Gt => BinOpCategory::Comparison,
+            And | Or => BinOpCategory::Shortcircuit,
+        }
+    }
+}
 
-            hir::BinOpKind::And | hir::BinOpKind::Or => BinOpCategory::Shortcircuit,
+impl From<hir::AssignOpKind> for BinOpCategory {
+    fn from(op: hir::AssignOpKind) -> BinOpCategory {
+        use hir::AssignOpKind::*;
+        match op {
+            ShlAssign | ShrAssign => BinOpCategory::Shift,
+            AddAssign | SubAssign | MulAssign | DivAssign | RemAssign => BinOpCategory::Math,
+            BitXorAssign | BitAndAssign | BitOrAssign => BinOpCategory::Bitwise,
         }
     }
 }
 
-/// Whether the binary operation is an assignment (`a += b`), or not (`a + b`)
+/// An assignment op (e.g. `a += b`), or a binary op (e.g. `a + b`).
 #[derive(Clone, Copy, Debug, PartialEq)]
-enum IsAssign {
-    No,
-    Yes,
+enum Op {
+    BinOp(hir::BinOp),
+    AssignOp(hir::AssignOp),
 }
 
-#[derive(Clone, Copy, Debug)]
-enum Op {
-    Binary(hir::BinOp, IsAssign),
-    Unary(hir::UnOp, Span),
+impl Op {
+    fn span(&self) -> Span {
+        match self {
+            Op::BinOp(op) => op.span,
+            Op::AssignOp(op) => op.span,
+        }
+    }
+
+    fn as_str(&self) -> &'static str {
+        match self {
+            Op::BinOp(op) => op.node.as_str(),
+            Op::AssignOp(op) => op.node.as_str(),
+        }
+    }
+
+    fn is_by_value(&self) -> bool {
+        match self {
+            Op::BinOp(op) => op.node.is_by_value(),
+            Op::AssignOp(op) => op.node.is_by_value(),
+        }
+    }
 }
 
 /// Dereferences a single level of immutable referencing.
@@ -1127,27 +1140,24 @@ fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> {
 /// Reason #2 is the killer. I tried for a while to always use
 /// overloaded logic and just check the types in constants/codegen after
 /// the fact, and it worked fine, except for SIMD types. -nmatsakis
-fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool {
+fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, category: BinOpCategory) -> bool {
     // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
     // (See https://github.com/rust-lang/rust/issues/57447.)
     let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
 
-    match BinOpCategory::from(op) {
+    match category.into() {
         BinOpCategory::Shortcircuit => true,
-
         BinOpCategory::Shift => {
             lhs.references_error()
                 || rhs.references_error()
                 || lhs.is_integral() && rhs.is_integral()
         }
-
         BinOpCategory::Math => {
             lhs.references_error()
                 || rhs.references_error()
                 || lhs.is_integral() && rhs.is_integral()
                 || lhs.is_floating_point() && rhs.is_floating_point()
         }
-
         BinOpCategory::Bitwise => {
             lhs.references_error()
                 || rhs.references_error()
@@ -1155,7 +1165,6 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool
                 || lhs.is_floating_point() && rhs.is_floating_point()
                 || lhs.is_bool() && rhs.is_bool()
         }
-
         BinOpCategory::Comparison => {
             lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar()
         }
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index f1f956779c9..9766ceda569 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -632,10 +632,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             {
                 let tcx = self.tcx;
                 trace!(?lt.hir_id.local_id, "polymorphic byte string lit");
-                self.typeck_results
-                    .borrow_mut()
-                    .treat_byte_string_as_slice
-                    .insert(lt.hir_id.local_id);
                 pat_ty =
                     Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, Ty::new_slice(tcx, tcx.types.u8));
             }
@@ -944,10 +940,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) {
         let var_ty = self.local_ty(span, var_id);
         if let Err(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) {
-            let hir = self.tcx.hir();
             let var_ty = self.resolve_vars_if_possible(var_ty);
             let msg = format!("first introduced with type `{var_ty}` here");
-            err.span_label(hir.span(var_id), msg);
+            err.span_label(self.tcx.hir_span(var_id), msg);
             let in_match = self.tcx.hir_parent_iter(var_id).any(|(_, n)| {
                 matches!(
                     n,
@@ -1255,7 +1250,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 })) => match self.tcx.hir_node(body_id.hir_id) {
                     hir::Node::Expr(expr) => {
                         if hir::is_range_literal(expr) {
-                            let span = self.tcx.hir().span(body_id.hir_id);
+                            let span = self.tcx.hir_span(body_id.hir_id);
                             if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) {
                                 e.span_suggestion_verbose(
                                     ident.span,
@@ -1286,7 +1281,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         pat_ty: Ty<'tcx>,
         segments: &'tcx [hir::PathSegment<'tcx>],
     ) {
-        if let Some(span) = self.tcx.hir().res_span(pat_res) {
+        if let Some(span) = self.tcx.hir_res_span(pat_res) {
             e.span_label(span, format!("{} defined here", res.descr()));
             if let [hir::PathSegment { ident, .. }] = &*segments {
                 e.span_label(
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index d07bfade157..8ab71e5220b 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -85,7 +85,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 /// Intermediate format to store the hir_id pointing to the use that resulted in the
 /// corresponding place being captured and a String which contains the captured value's
 /// name (i.e: a.b.c)
-#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
 enum UpvarMigrationInfo {
     /// We previously captured all of `x`, but now we capture some sub-path.
     CapturingPrecise { source_expr: Option<HirId>, var_name: String },
@@ -635,7 +635,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let (place, capture_kind) = truncate_capture_for_optimization(place, capture_kind);
 
                 let usage_span = if let Some(usage_expr) = capture_info.path_expr_id {
-                    self.tcx.hir().span(usage_expr)
+                    self.tcx.hir_span(usage_expr)
                 } else {
                     unreachable!()
                 };
@@ -986,7 +986,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         for lint_note in diagnostics_info.iter() {
                             match &lint_note.captures_info {
                                 UpvarMigrationInfo::CapturingPrecise { source_expr: Some(capture_expr_id), var_name: captured_name } => {
-                                    let cause_span = self.tcx.hir().span(*capture_expr_id);
+                                    let cause_span = self.tcx.hir_span(*capture_expr_id);
                                     lint.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`",
                                         self.tcx.hir_name(*var_hir_id),
                                         captured_name,
@@ -1047,13 +1047,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         "add a dummy let to cause {migrated_variables_concat} to be fully captured"
                     );
 
-                    let closure_span = self.tcx.hir().span_with_body(closure_hir_id);
+                    let closure_span = self.tcx.hir_span_with_body(closure_hir_id);
                     let mut closure_body_span = {
                         // If the body was entirely expanded from a macro
                         // invocation, i.e. the body is not contained inside the
                         // closure span, then we walk up the expansion until we
                         // find the span before the expansion.
-                        let s = self.tcx.hir().span_with_body(body_id.hir_id);
+                        let s = self.tcx.hir_span_with_body(body_id.hir_id);
                         s.find_ancestor_inside(closure_span).unwrap_or(s)
                     };
 
@@ -1396,14 +1396,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 FxIndexSet::default()
             };
 
-            // Combine all the captures responsible for needing migrations into one HashSet
+            // Combine all the captures responsible for needing migrations into one IndexSet
             let mut capture_diagnostic = drop_reorder_diagnostic.clone();
             for key in auto_trait_diagnostic.keys() {
                 capture_diagnostic.insert(key.clone());
             }
 
             let mut capture_diagnostic = capture_diagnostic.into_iter().collect::<Vec<_>>();
-            capture_diagnostic.sort();
+            capture_diagnostic.sort_by_cached_key(|info| match info {
+                UpvarMigrationInfo::CapturingPrecise { source_expr: _, var_name } => {
+                    (0, Some(var_name.clone()))
+                }
+                UpvarMigrationInfo::CapturingNothing { use_span: _ } => (1, None),
+            });
             for captures_info in capture_diagnostic {
                 // Get the auto trait reasons of why migration is needed because of that capture, if there are any
                 let capture_trait_reasons =
@@ -1752,8 +1757,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let capture_str = construct_capture_info_string(self.tcx, place, capture_info);
                 let output_str = format!("Capturing {capture_str}");
 
-                let span =
-                    capture_info.path_expr_id.map_or(closure_span, |e| self.tcx.hir().span(e));
+                let span = capture_info.path_expr_id.map_or(closure_span, |e| self.tcx.hir_span(e));
                 diag.span_note(span, output_str);
             }
             diag.emit();
@@ -1780,10 +1784,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         if capture.info.path_expr_id != capture.info.capture_kind_expr_id {
                             let path_span = capture_info
                                 .path_expr_id
-                                .map_or(closure_span, |e| self.tcx.hir().span(e));
+                                .map_or(closure_span, |e| self.tcx.hir_span(e));
                             let capture_kind_span = capture_info
                                 .capture_kind_expr_id
-                                .map_or(closure_span, |e| self.tcx.hir().span(e));
+                                .map_or(closure_span, |e| self.tcx.hir_span(e));
 
                             let mut multi_span: MultiSpan =
                                 MultiSpan::from_spans(vec![path_span, capture_kind_span]);
@@ -1799,7 +1803,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         } else {
                             let span = capture_info
                                 .path_expr_id
-                                .map_or(closure_span, |e| self.tcx.hir().span(e));
+                                .map_or(closure_span, |e| self.tcx.hir_span(e));
 
                             diag.span_note(span, output_str);
                         };
@@ -1828,8 +1832,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let mut is_mutbl = bm.1;
 
         for pointer_ty in place.deref_tys() {
-            match self.structurally_resolve_type(self.tcx.hir().span(var_hir_id), pointer_ty).kind()
-            {
+            match self.structurally_resolve_type(self.tcx.hir_span(var_hir_id), pointer_ty).kind() {
                 // We don't capture derefs of raw ptrs
                 ty::RawPtr(_, _) => unreachable!(),
 
@@ -1844,7 +1847,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 ty::Adt(def, ..) if def.is_box() => {}
 
                 unexpected_ty => span_bug!(
-                    self.tcx.hir().span(var_hir_id),
+                    self.tcx.hir_span(var_hir_id),
                     "deref of unexpected pointer type {:?}",
                     unexpected_ty
                 ),
@@ -1975,14 +1978,14 @@ fn drop_location_span(tcx: TyCtxt<'_>, hir_id: HirId) -> Span {
     let owner_node = tcx.hir_node(owner_id);
     let owner_span = match owner_node {
         hir::Node::Item(item) => match item.kind {
-            hir::ItemKind::Fn { body: owner_id, .. } => tcx.hir().span(owner_id.hir_id),
+            hir::ItemKind::Fn { body: owner_id, .. } => tcx.hir_span(owner_id.hir_id),
             _ => {
                 bug!("Drop location span error: need to handle more ItemKind '{:?}'", item.kind);
             }
         },
-        hir::Node::Block(block) => tcx.hir().span(block.hir_id),
-        hir::Node::TraitItem(item) => tcx.hir().span(item.hir_id()),
-        hir::Node::ImplItem(item) => tcx.hir().span(item.hir_id()),
+        hir::Node::Block(block) => tcx.hir_span(block.hir_id),
+        hir::Node::TraitItem(item) => tcx.hir_span(item.hir_id()),
+        hir::Node::ImplItem(item) => tcx.hir_span(item.hir_id()),
         _ => {
             bug!("Drop location span error: need to handle more Node '{:?}'", owner_node);
         }
@@ -2325,8 +2328,9 @@ fn should_do_rust_2021_incompatible_closure_captures_analysis(
         return false;
     }
 
-    let (level, _) =
-        tcx.lint_level_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id);
+    let level = tcx
+        .lint_level_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id)
+        .level;
 
     !matches!(level, lint::Level::Allow)
 }
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index b63c0b6ab7e..992d881374f 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -81,9 +81,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports);
         wbcx.typeck_results.used_trait_imports = used_trait_imports;
 
-        wbcx.typeck_results.treat_byte_string_as_slice =
-            mem::take(&mut self.typeck_results.borrow_mut().treat_byte_string_as_slice);
-
         debug!("writeback: typeck results for {:?} are {:#?}", item_def_id, wbcx.typeck_results);
 
         self.tcx.arena.alloc(wbcx.typeck_results)
@@ -160,7 +157,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
                     self.typeck_results.node_args_mut().remove(e.hir_id);
                 }
             }
-            hir::ExprKind::Binary(ref op, lhs, rhs) | hir::ExprKind::AssignOp(ref op, lhs, rhs) => {
+            hir::ExprKind::Binary(ref op, lhs, rhs) => {
                 let lhs_ty = self.typeck_results.node_type(lhs.hir_id);
                 let rhs_ty = self.typeck_results.node_type(rhs.hir_id);
 
@@ -168,25 +165,27 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
                     self.typeck_results.type_dependent_defs_mut().remove(e.hir_id);
                     self.typeck_results.node_args_mut().remove(e.hir_id);
 
-                    match e.kind {
-                        hir::ExprKind::Binary(..) => {
-                            if !op.node.is_by_value() {
-                                let mut adjustments = self.typeck_results.adjustments_mut();
-                                if let Some(a) = adjustments.get_mut(lhs.hir_id) {
-                                    a.pop();
-                                }
-                                if let Some(a) = adjustments.get_mut(rhs.hir_id) {
-                                    a.pop();
-                                }
-                            }
+                    if !op.node.is_by_value() {
+                        let mut adjustments = self.typeck_results.adjustments_mut();
+                        if let Some(a) = adjustments.get_mut(lhs.hir_id) {
+                            a.pop();
                         }
-                        hir::ExprKind::AssignOp(..)
-                            if let Some(a) =
-                                self.typeck_results.adjustments_mut().get_mut(lhs.hir_id) =>
-                        {
+                        if let Some(a) = adjustments.get_mut(rhs.hir_id) {
                             a.pop();
                         }
-                        _ => {}
+                    }
+                }
+            }
+            hir::ExprKind::AssignOp(_, lhs, rhs) => {
+                let lhs_ty = self.typeck_results.node_type(lhs.hir_id);
+                let rhs_ty = self.typeck_results.node_type(rhs.hir_id);
+
+                if lhs_ty.is_scalar() && rhs_ty.is_scalar() {
+                    self.typeck_results.type_dependent_defs_mut().remove(e.hir_id);
+                    self.typeck_results.node_args_mut().remove(e.hir_id);
+
+                    if let Some(a) = self.typeck_results.adjustments_mut().get_mut(lhs.hir_id) {
+                        a.pop();
                     }
                 }
             }
@@ -491,7 +490,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
 
                 if let ty::UserTypeKind::TypeOf(_, user_args) = c_ty.value.kind {
                     // This is a unit-testing mechanism.
-                    let span = self.tcx().hir().span(hir_id);
+                    let span = self.tcx().hir_span(hir_id);
                     // We need to buffer the errors in order to guarantee a consistent
                     // order when emitting them.
                     let err =
@@ -775,7 +774,7 @@ impl Locatable for Span {
 
 impl Locatable for HirId {
     fn to_span(&self, tcx: TyCtxt<'_>) -> Span {
-        tcx.hir().span(*self)
+        tcx.hir_span(*self)
     }
 }
 
diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs
index 215b1333726..d9297fb4194 100644
--- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs
@@ -198,13 +198,12 @@ impl<'tcx> InferCtxt<'tcx> {
     /// it hasn't previously been defined. This does not emit any
     /// constraints and it's the responsibility of the caller to make
     /// sure that the item bounds of the opaque are checked.
-    pub fn inject_new_hidden_type_unchecked(
+    pub fn register_hidden_type_in_storage(
         &self,
         opaque_type_key: OpaqueTypeKey<'tcx>,
         hidden_ty: OpaqueHiddenType<'tcx>,
-    ) {
-        let prev = self.inner.borrow_mut().opaque_types().register(opaque_type_key, hidden_ty);
-        assert_eq!(prev, None);
+    ) -> Option<Ty<'tcx>> {
+        self.inner.borrow_mut().opaque_types().register(opaque_type_key, hidden_ty)
     }
 
     /// Insert a hidden type into the opaque type storage, equating it
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index 3f87b1a547b..33b4a48b28d 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -204,6 +204,14 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
                     error!("`cfg()` names cannot be after values");
                 }
                 names.push(ident);
+            } else if let Some(boolean) = arg.boolean_literal() {
+                if values_specified {
+                    error!("`cfg()` names cannot be after values");
+                }
+                names.push(rustc_span::Ident::new(
+                    if boolean { rustc_span::kw::True } else { rustc_span::kw::False },
+                    arg.span(),
+                ));
             } else if arg.has_name(sym::any)
                 && let Some(args) = arg.meta_item_list()
             {
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 93013c8b3f6..747e36b6a1a 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -976,7 +976,7 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
         tcx.par_hir_body_owners(|def_id| {
             if tcx.is_coroutine(def_id.to_def_id()) {
                 tcx.ensure_ok().mir_coroutine_witnesses(def_id);
-                tcx.ensure_ok().check_coroutine_obligations(
+                let _ = tcx.ensure_ok().check_coroutine_obligations(
                     tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(),
                 );
                 // Eagerly check the unsubstituted layout for cycles.
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index c56dbc2e1c4..dae0efcbbc4 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -29,6 +29,7 @@ use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
 use rustc_hir::intravisit::FnKind as HirFnKind;
 use rustc_hir::{Body, FnDecl, GenericParamKind, PatKind, PredicateOrigin};
 use rustc_middle::bug;
+use rustc_middle::lint::LevelAndSource;
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, Upcast, VariantDef};
@@ -694,7 +695,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations {
         }
 
         // Avoid listing trait impls if the trait is allowed.
-        let (level, _) = cx.tcx.lint_level_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id());
+        let LevelAndSource { level, .. } =
+            cx.tcx.lint_level_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id());
         if level == Level::Allow {
             return;
         }
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 017ae943e91..885a7308bdc 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -17,13 +17,12 @@ use rustc_hir::def_id::{CrateNum, DefId};
 use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
 use rustc_hir::{Pat, PatKind};
 use rustc_middle::bug;
+use rustc_middle::lint::LevelAndSource;
 use rustc_middle::middle::privacy::EffectiveVisibilities;
 use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
 use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths};
 use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode};
-use rustc_session::lint::{
-    FutureIncompatibleInfo, Level, Lint, LintBuffer, LintExpectationId, LintId,
-};
+use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintBuffer, LintExpectationId, LintId};
 use rustc_session::{LintStoreMarker, Session};
 use rustc_span::edit_distance::find_best_match_for_names;
 use rustc_span::{Ident, Span, Symbol, sym};
@@ -573,7 +572,7 @@ pub trait LintContext {
     }
 
     /// This returns the lint level for the given lint at the current location.
-    fn get_lint_level(&self, lint: &'static Lint) -> Level;
+    fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource;
 
     /// This function can be used to manually fulfill an expectation. This can
     /// be used for lints which contain several spans, and should be suppressed,
@@ -642,8 +641,8 @@ impl<'tcx> LintContext for LateContext<'tcx> {
         }
     }
 
-    fn get_lint_level(&self, lint: &'static Lint) -> Level {
-        self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs).0
+    fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource {
+        self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs)
     }
 }
 
@@ -663,8 +662,8 @@ impl LintContext for EarlyContext<'_> {
         self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorate)
     }
 
-    fn get_lint_level(&self, lint: &'static Lint) -> Level {
-        self.builder.lint_level(lint).0
+    fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource {
+        self.builder.lint_level(lint)
     }
 }
 
diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
index c2404a7b843..26481b97076 100644
--- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs
+++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
@@ -392,7 +392,7 @@ where
                         }
                         _ => {
                             self.tcx.dcx().span_delayed_bug(
-                                self.tcx.hir().span(arg.hir_id()),
+                                self.tcx.hir_span(arg.hir_id()),
                                 "no valid for captured arg",
                             );
                         }
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index 8718fb807ec..f1fe07cfcfa 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -84,10 +84,10 @@ impl LintLevelSets {
     ) -> LevelAndSource {
         let lint = LintId::of(lint);
         let (level, mut src) = self.raw_lint_id_level(lint, idx, aux);
-        let level = reveal_actual_level(level, &mut src, sess, lint, |id| {
+        let (level, lint_id) = reveal_actual_level(level, &mut src, sess, lint, |id| {
             self.raw_lint_id_level(id, idx, aux)
         });
-        (level, src)
+        LevelAndSource { level, lint_id, src }
     }
 
     fn raw_lint_id_level(
@@ -95,17 +95,17 @@ impl LintLevelSets {
         id: LintId,
         mut idx: LintStackIndex,
         aux: Option<&FxIndexMap<LintId, LevelAndSource>>,
-    ) -> (Option<Level>, LintLevelSource) {
+    ) -> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource) {
         if let Some(specs) = aux
-            && let Some(&(level, src)) = specs.get(&id)
+            && let Some(&LevelAndSource { level, lint_id, src }) = specs.get(&id)
         {
-            return (Some(level), src);
+            return (Some((level, lint_id)), src);
         }
 
         loop {
             let LintSet { ref specs, parent } = self.list[idx];
-            if let Some(&(level, src)) = specs.get(&id) {
-                return (Some(level), src);
+            if let Some(&LevelAndSource { level, lint_id, src }) = specs.get(&id) {
+                return (Some((level, lint_id)), src);
             }
             if idx == COMMAND_LINE {
                 return (None, LintLevelSource::Default);
@@ -131,8 +131,8 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LintId> {
         })
         .filter_map(|lint| {
             let lint_level = map.lint_level_id_at_node(tcx, LintId::of(lint), CRATE_HIR_ID);
-            if matches!(lint_level, (Level::Allow, ..))
-                || (matches!(lint_level, (.., LintLevelSource::Default)))
+            if matches!(lint_level.level, Level::Allow)
+                || (matches!(lint_level.src, LintLevelSource::Default))
                     && lint.default_level(tcx.sess.edition()) == Level::Allow
             {
                 Some(LintId::of(lint))
@@ -379,13 +379,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> {
     fn visit_attribute(&mut self, attribute: &'tcx hir::Attribute) {
         if matches!(
             Level::from_attr(attribute),
-            Some(
-                Level::Warn
-                    | Level::Deny
-                    | Level::Forbid
-                    | Level::Expect(..)
-                    | Level::ForceWarn(..),
-            )
+            Some((Level::Warn | Level::Deny | Level::Forbid | Level::Expect | Level::ForceWarn, _))
         ) {
             let store = unerased_lint_store(self.tcx.sess);
             // Lint attributes are always a metalist inside a
@@ -450,6 +444,19 @@ impl<'s> LintLevelsBuilder<'s, TopDown> {
         builder
     }
 
+    pub fn crate_root(
+        sess: &'s Session,
+        features: &'s Features,
+        lint_added_lints: bool,
+        store: &'s LintStore,
+        registered_tools: &'s RegisteredTools,
+        crate_attrs: &[ast::Attribute],
+    ) -> Self {
+        let mut builder = Self::new(sess, features, lint_added_lints, store, registered_tools);
+        builder.add(crate_attrs, true, None);
+        builder
+    }
+
     fn process_command_line(&mut self) {
         self.provider.cur = self
             .provider
@@ -528,9 +535,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
         for &(ref lint_name, level) in &self.sess.opts.lint_opts {
             // Checks the validity of lint names derived from the command line.
             let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
-            if lint_name_only == crate::WARNINGS.name_lower()
-                && matches!(level, Level::ForceWarn(_))
-            {
+            if lint_name_only == crate::WARNINGS.name_lower() && matches!(level, Level::ForceWarn) {
                 self.sess
                     .dcx()
                     .emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
@@ -573,7 +578,6 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                 _ => {}
             };
 
-            let orig_level = level;
             let lint_flag_val = Symbol::intern(lint_name);
 
             let Ok(ids) = self.store.find_lints(lint_name) else {
@@ -582,15 +586,15 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
             };
             for id in ids {
                 // ForceWarn and Forbid cannot be overridden
-                if let Some((Level::ForceWarn(_) | Level::Forbid, _)) =
+                if let Some(LevelAndSource { level: Level::ForceWarn | Level::Forbid, .. }) =
                     self.current_specs().get(&id)
                 {
                     continue;
                 }
 
                 if self.check_gated_lint(id, DUMMY_SP, true) {
-                    let src = LintLevelSource::CommandLine(lint_flag_val, orig_level);
-                    self.insert(id, (level, src));
+                    let src = LintLevelSource::CommandLine(lint_flag_val, level);
+                    self.insert(id, LevelAndSource { level, lint_id: None, src });
                 }
             }
         }
@@ -599,8 +603,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
     /// Attempts to insert the `id` to `level_src` map entry. If unsuccessful
     /// (e.g. if a forbid was already inserted on the same scope), then emits a
     /// diagnostic with no change to `specs`.
-    fn insert_spec(&mut self, id: LintId, (level, src): LevelAndSource) {
-        let (old_level, old_src) = self.provider.get_lint_level(id.lint, self.sess);
+    fn insert_spec(&mut self, id: LintId, LevelAndSource { level, lint_id, src }: LevelAndSource) {
+        let LevelAndSource { level: old_level, src: old_src, .. } =
+            self.provider.get_lint_level(id.lint, self.sess);
 
         // Setting to a non-forbid level is an error if the lint previously had
         // a forbid level. Note that this is not necessarily true even with a
@@ -672,7 +677,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
         // The lint `unfulfilled_lint_expectations` can't be expected, as it would suppress itself.
         // Handling expectations of this lint would add additional complexity with little to no
         // benefit. The expect level for this lint will therefore be ignored.
-        if let Level::Expect(_) = level
+        if let Level::Expect = level
             && id == LintId::of(UNFULFILLED_LINT_EXPECTATIONS)
         {
             return;
@@ -680,13 +685,16 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
 
         match (old_level, level) {
             // If the new level is an expectation store it in `ForceWarn`
-            (Level::ForceWarn(_), Level::Expect(expectation_id)) => {
-                self.insert(id, (Level::ForceWarn(Some(expectation_id)), old_src))
+            (Level::ForceWarn, Level::Expect) => {
+                self.insert(id, LevelAndSource { level: Level::ForceWarn, lint_id, src: old_src })
             }
             // Keep `ForceWarn` level but drop the expectation
-            (Level::ForceWarn(_), _) => self.insert(id, (Level::ForceWarn(None), old_src)),
+            (Level::ForceWarn, _) => self.insert(
+                id,
+                LevelAndSource { level: Level::ForceWarn, lint_id: None, src: old_src },
+            ),
             // Set the lint level as normal
-            _ => self.insert(id, (level, src)),
+            _ => self.insert(id, LevelAndSource { level, lint_id, src }),
         };
     }
 
@@ -701,7 +709,11 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
             if attr.has_name(sym::automatically_derived) {
                 self.insert(
                     LintId::of(SINGLE_USE_LIFETIMES),
-                    (Level::Allow, LintLevelSource::Default),
+                    LevelAndSource {
+                        level: Level::Allow,
+                        lint_id: None,
+                        src: LintLevelSource::Default,
+                    },
                 );
                 continue;
             }
@@ -712,15 +724,22 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                     .meta_item_list()
                     .is_some_and(|l| ast::attr::list_contains_name(&l, sym::hidden))
             {
-                self.insert(LintId::of(MISSING_DOCS), (Level::Allow, LintLevelSource::Default));
+                self.insert(
+                    LintId::of(MISSING_DOCS),
+                    LevelAndSource {
+                        level: Level::Allow,
+                        lint_id: None,
+                        src: LintLevelSource::Default,
+                    },
+                );
                 continue;
             }
 
-            let level = match Level::from_attr(attr) {
+            let (level, lint_id) = match Level::from_attr(attr) {
                 None => continue,
                 // This is the only lint level with a `LintExpectationId` that can be created from
                 // an attribute.
-                Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => {
+                Some((Level::Expect, Some(unstable_id))) if let Some(hir_id) = source_hir_id => {
                     let LintExpectationId::Unstable { lint_index: None, attr_id: _ } = unstable_id
                     else {
                         bug!("stable id Level::from_attr")
@@ -732,9 +751,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                         lint_index: None,
                     };
 
-                    Level::Expect(stable_id)
+                    (Level::Expect, Some(stable_id))
                 }
-                Some(lvl) => lvl,
+                Some((lvl, id)) => (lvl, id),
             };
 
             let Some(mut metas) = attr.meta_item_list() else { continue };
@@ -782,13 +801,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
             }
 
             for (lint_index, li) in metas.iter_mut().enumerate() {
-                let level = match level {
-                    Level::Expect(mut id) => {
-                        id.set_lint_index(Some(lint_index as u16));
-                        Level::Expect(id)
-                    }
-                    level => level,
-                };
+                let mut lint_id = lint_id;
+                if let Some(id) = &mut lint_id {
+                    id.set_lint_index(Some(lint_index as u16));
+                }
 
                 let sp = li.span();
                 let meta_item = match li {
@@ -920,7 +936,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                 let src = LintLevelSource::Node { name, span: sp, reason };
                 for &id in ids {
                     if self.check_gated_lint(id, sp, false) {
-                        self.insert_spec(id, (level, src));
+                        self.insert_spec(id, LevelAndSource { level, lint_id, src });
                     }
                 }
 
@@ -929,7 +945,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                 // overriding the lint level but instead add an expectation that can't be
                 // fulfilled. The lint message will include an explanation, that the
                 // `unfulfilled_lint_expectations` lint can't be expected.
-                if let Level::Expect(expect_id) = level {
+                if let (Level::Expect, Some(expect_id)) = (level, lint_id) {
                     // The `unfulfilled_lint_expectations` lint is not part of any lint
                     // groups. Therefore. we only need to check the slice if it contains a
                     // single lint.
@@ -951,7 +967,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
         }
 
         if self.lint_added_lints && !is_crate_node {
-            for (id, &(level, ref src)) in self.current_specs().iter() {
+            for (id, &LevelAndSource { level, ref src, .. }) in self.current_specs().iter() {
                 if !id.lint.crate_level_only {
                     continue;
                 }
@@ -989,10 +1005,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
 
         if self.lint_added_lints {
             let lint = builtin::UNKNOWN_LINTS;
-            let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS);
+            let level = self.lint_level(builtin::UNKNOWN_LINTS);
             // FIXME: make this translatable
             #[allow(rustc::diagnostic_outside_of_impl)]
-            lint_level(self.sess, lint, level, src, Some(span.into()), |lint| {
+            lint_level(self.sess, lint, level, Some(span.into()), |lint| {
                 lint.primary_message(fluent::lint_unknown_gated_lint);
                 lint.arg("name", lint_id.lint.name_lower());
                 lint.note(fluent::lint_note);
@@ -1027,8 +1043,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
         span: Option<MultiSpan>,
         decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
     ) {
-        let (level, src) = self.lint_level(lint);
-        lint_level(self.sess, lint, level, src, span, decorate)
+        let level = self.lint_level(lint);
+        lint_level(self.sess, lint, level, span, decorate)
     }
 
     #[track_caller]
@@ -1038,16 +1054,16 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
         span: MultiSpan,
         decorate: impl for<'a> LintDiagnostic<'a, ()>,
     ) {
-        let (level, src) = self.lint_level(lint);
-        lint_level(self.sess, lint, level, src, Some(span), |lint| {
+        let level = self.lint_level(lint);
+        lint_level(self.sess, lint, level, Some(span), |lint| {
             decorate.decorate_lint(lint);
         });
     }
 
     #[track_caller]
     pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> LintDiagnostic<'a, ()>) {
-        let (level, src) = self.lint_level(lint);
-        lint_level(self.sess, lint, level, src, None, |lint| {
+        let level = self.lint_level(lint);
+        lint_level(self.sess, lint, level, None, |lint| {
             decorate.decorate_lint(lint);
         });
     }
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 25878c7ac81..9b5c564d332 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -130,6 +130,7 @@ pub use context::{
 };
 pub use early::{EarlyCheckNode, check_ast_node};
 pub use late::{check_crate, late_lint_mod, unerased_lint_store};
+pub use levels::LintLevelsBuilder;
 pub use passes::{EarlyLintPass, LateLintPass};
 pub use rustc_session::lint::Level::{self, *};
 pub use rustc_session::lint::{
diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs
index 66e207a451e..9c11fb41aa6 100644
--- a/compiler/rustc_lint/src/non_ascii_idents.rs
+++ b/compiler/rustc_lint/src/non_ascii_idents.rs
@@ -159,12 +159,13 @@ impl EarlyLintPass for NonAsciiIdents {
         use rustc_span::Span;
         use unicode_security::GeneralSecurityProfile;
 
-        let check_non_ascii_idents = cx.builder.lint_level(NON_ASCII_IDENTS).0 != Level::Allow;
+        let check_non_ascii_idents = cx.builder.lint_level(NON_ASCII_IDENTS).level != Level::Allow;
         let check_uncommon_codepoints =
-            cx.builder.lint_level(UNCOMMON_CODEPOINTS).0 != Level::Allow;
-        let check_confusable_idents = cx.builder.lint_level(CONFUSABLE_IDENTS).0 != Level::Allow;
+            cx.builder.lint_level(UNCOMMON_CODEPOINTS).level != Level::Allow;
+        let check_confusable_idents =
+            cx.builder.lint_level(CONFUSABLE_IDENTS).level != Level::Allow;
         let check_mixed_script_confusables =
-            cx.builder.lint_level(MIXED_SCRIPT_CONFUSABLES).0 != Level::Allow;
+            cx.builder.lint_level(MIXED_SCRIPT_CONFUSABLES).level != Level::Allow;
 
         if !check_non_ascii_idents
             && !check_uncommon_codepoints
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 6966985bdf0..a6c82f574a1 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -14,7 +14,7 @@ use rustc_middle::ty::{
 };
 use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
 use rustc_span::def_id::LocalDefId;
-use rustc_span::{Span, Symbol, source_map, sym};
+use rustc_span::{Span, Symbol, sym};
 use tracing::debug;
 use {rustc_ast as ast, rustc_hir as hir};
 
@@ -223,7 +223,7 @@ impl TypeLimits {
 fn lint_nan<'tcx>(
     cx: &LateContext<'tcx>,
     e: &'tcx hir::Expr<'tcx>,
-    binop: hir::BinOp,
+    binop: hir::BinOpKind,
     l: &'tcx hir::Expr<'tcx>,
     r: &'tcx hir::Expr<'tcx>,
 ) {
@@ -262,19 +262,19 @@ fn lint_nan<'tcx>(
         InvalidNanComparisons::EqNe { suggestion }
     }
 
-    let lint = match binop.node {
+    let lint = match binop {
         hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => {
             eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
                 nan_plus_binop: l_span.until(r_span),
                 float: r_span.shrink_to_hi(),
-                neg: (binop.node == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()),
+                neg: (binop == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()),
             })
         }
         hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, r) => {
             eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
                 nan_plus_binop: l_span.shrink_to_hi().to(r_span),
                 float: l_span.shrink_to_hi(),
-                neg: (binop.node == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()),
+                neg: (binop == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()),
             })
         }
         hir::BinOpKind::Lt | hir::BinOpKind::Le | hir::BinOpKind::Gt | hir::BinOpKind::Ge
@@ -560,11 +560,11 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
                 }
             }
             hir::ExprKind::Binary(binop, ref l, ref r) => {
-                if is_comparison(binop) {
-                    if !check_limits(cx, binop, l, r) {
+                if is_comparison(binop.node) {
+                    if !check_limits(cx, binop.node, l, r) {
                         cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
                     } else {
-                        lint_nan(cx, e, binop, l, r);
+                        lint_nan(cx, e, binop.node, l, r);
                         let cmpop = ComparisonOp::BinOp(binop.node);
                         lint_wide_pointer(cx, e, cmpop, l, r);
                         lint_fn_pointer(cx, e, cmpop, l, r);
@@ -591,8 +591,8 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
             _ => {}
         };
 
-        fn is_valid<T: PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
-            match binop.node {
+        fn is_valid<T: PartialOrd>(binop: hir::BinOpKind, v: T, min: T, max: T) -> bool {
+            match binop {
                 hir::BinOpKind::Lt => v > min && v <= max,
                 hir::BinOpKind::Le => v >= min && v < max,
                 hir::BinOpKind::Gt => v >= min && v < max,
@@ -602,22 +602,19 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
             }
         }
 
-        fn rev_binop(binop: hir::BinOp) -> hir::BinOp {
-            source_map::respan(
-                binop.span,
-                match binop.node {
-                    hir::BinOpKind::Lt => hir::BinOpKind::Gt,
-                    hir::BinOpKind::Le => hir::BinOpKind::Ge,
-                    hir::BinOpKind::Gt => hir::BinOpKind::Lt,
-                    hir::BinOpKind::Ge => hir::BinOpKind::Le,
-                    _ => return binop,
-                },
-            )
+        fn rev_binop(binop: hir::BinOpKind) -> hir::BinOpKind {
+            match binop {
+                hir::BinOpKind::Lt => hir::BinOpKind::Gt,
+                hir::BinOpKind::Le => hir::BinOpKind::Ge,
+                hir::BinOpKind::Gt => hir::BinOpKind::Lt,
+                hir::BinOpKind::Ge => hir::BinOpKind::Le,
+                _ => binop,
+            }
         }
 
         fn check_limits(
             cx: &LateContext<'_>,
-            binop: hir::BinOp,
+            binop: hir::BinOpKind,
             l: &hir::Expr<'_>,
             r: &hir::Expr<'_>,
         ) -> bool {
@@ -659,9 +656,9 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
             }
         }
 
-        fn is_comparison(binop: hir::BinOp) -> bool {
+        fn is_comparison(binop: hir::BinOpKind) -> bool {
             matches!(
-                binop.node,
+                binop,
                 hir::BinOpKind::Eq
                     | hir::BinOpKind::Lt
                     | hir::BinOpKind::Le
@@ -1403,7 +1400,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
             CItemKind::Definition => "fn",
         };
         let span_note = if let ty::Adt(def, _) = ty.kind()
-            && let Some(sp) = self.cx.tcx.hir().span_if_local(def.did())
+            && let Some(sp) = self.cx.tcx.hir_span_if_local(def.did())
         {
             Some(sp)
         } else {
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 46b4b1d4383..7fdbae3a59d 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -8,7 +8,8 @@ use rustc_data_structures::stable_hasher::{
 };
 use rustc_error_messages::{DiagMessage, MultiSpan};
 use rustc_hir::def::Namespace;
-use rustc_hir::{HashStableContext, HirId, MissingLifetimeKind};
+use rustc_hir::def_id::DefPathHash;
+use rustc_hir::{HashStableContext, HirId, ItemLocalId, MissingLifetimeKind};
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 pub use rustc_span::edition::Edition;
 use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym};
@@ -102,7 +103,7 @@ pub enum Applicability {
 /// The index values have a type of `u16` to reduce the size of the `LintExpectationId`.
 /// It's reasonable to assume that no user will define 2^16 attributes on one node or
 /// have that amount of lints listed. `u16` values should therefore suffice.
-#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Encodable, Decodable)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Encodable, Decodable)]
 pub enum LintExpectationId {
     /// Used for lints emitted during the `EarlyLintPass`. This id is not
     /// hash stable and should not be cached.
@@ -156,13 +157,14 @@ impl<HCX: rustc_hir::HashStableContext> HashStable<HCX> for LintExpectationId {
 }
 
 impl<HCX: rustc_hir::HashStableContext> ToStableHashKey<HCX> for LintExpectationId {
-    type KeyType = (HirId, u16, u16);
+    type KeyType = (DefPathHash, ItemLocalId, u16, u16);
 
     #[inline]
-    fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
+    fn to_stable_hash_key(&self, hcx: &HCX) -> Self::KeyType {
         match self {
             LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => {
-                (*hir_id, *attr_index, *lint_index)
+                let (def_path_hash, lint_idx) = hir_id.to_stable_hash_key(hcx);
+                (def_path_hash, lint_idx, *attr_index, *lint_index)
             }
             _ => {
                 unreachable!("HashStable should only be called for a filled `LintExpectationId`")
@@ -199,9 +201,9 @@ pub enum Level {
     ///
     /// See RFC 2383.
     ///
-    /// The [`LintExpectationId`] is used to later link a lint emission to the actual
+    /// Requires a [`LintExpectationId`] to later link a lint emission to the actual
     /// expectation. It can be ignored in most cases.
-    Expect(LintExpectationId),
+    Expect,
     /// The `warn` level will produce a warning if the lint was violated, however the
     /// compiler will continue with its execution.
     Warn,
@@ -209,9 +211,9 @@ pub enum Level {
     /// to ensure that a lint can't be suppressed. This lint level can currently only be set
     /// via the console and is therefore session specific.
     ///
-    /// The [`LintExpectationId`] is intended to fulfill expectations marked via the
+    /// Requires a [`LintExpectationId`] to fulfill expectations marked via the
     /// `#[expect]` attribute, that will still be suppressed due to the level.
-    ForceWarn(Option<LintExpectationId>),
+    ForceWarn,
     /// The `deny` level will produce an error and stop further execution after the lint
     /// pass is complete.
     Deny,
@@ -225,9 +227,9 @@ impl Level {
     pub fn as_str(self) -> &'static str {
         match self {
             Level::Allow => "allow",
-            Level::Expect(_) => "expect",
+            Level::Expect => "expect",
             Level::Warn => "warn",
-            Level::ForceWarn(_) => "force-warn",
+            Level::ForceWarn => "force-warn",
             Level::Deny => "deny",
             Level::Forbid => "forbid",
         }
@@ -246,24 +248,30 @@ impl Level {
     }
 
     /// Converts an `Attribute` to a level.
-    pub fn from_attr(attr: &impl AttributeExt) -> Option<Self> {
+    pub fn from_attr(attr: &impl AttributeExt) -> Option<(Self, Option<LintExpectationId>)> {
         Self::from_symbol(attr.name_or_empty(), || Some(attr.id()))
     }
 
     /// Converts a `Symbol` to a level.
-    pub fn from_symbol(s: Symbol, id: impl FnOnce() -> Option<AttrId>) -> Option<Self> {
+    pub fn from_symbol(
+        s: Symbol,
+        id: impl FnOnce() -> Option<AttrId>,
+    ) -> Option<(Self, Option<LintExpectationId>)> {
         match s {
-            sym::allow => Some(Level::Allow),
+            sym::allow => Some((Level::Allow, None)),
             sym::expect => {
                 if let Some(attr_id) = id() {
-                    Some(Level::Expect(LintExpectationId::Unstable { attr_id, lint_index: None }))
+                    Some((
+                        Level::Expect,
+                        Some(LintExpectationId::Unstable { attr_id, lint_index: None }),
+                    ))
                 } else {
                     None
                 }
             }
-            sym::warn => Some(Level::Warn),
-            sym::deny => Some(Level::Deny),
-            sym::forbid => Some(Level::Forbid),
+            sym::warn => Some((Level::Warn, None)),
+            sym::deny => Some((Level::Deny, None)),
+            sym::forbid => Some((Level::Forbid, None)),
             _ => None,
         }
     }
@@ -274,8 +282,8 @@ impl Level {
             Level::Deny => "-D",
             Level::Forbid => "-F",
             Level::Allow => "-A",
-            Level::ForceWarn(_) => "--force-warn",
-            Level::Expect(_) => {
+            Level::ForceWarn => "--force-warn",
+            Level::Expect => {
                 unreachable!("the expect level does not have a commandline flag")
             }
         }
@@ -283,17 +291,10 @@ impl Level {
 
     pub fn is_error(self) -> bool {
         match self {
-            Level::Allow | Level::Expect(_) | Level::Warn | Level::ForceWarn(_) => false,
+            Level::Allow | Level::Expect | Level::Warn | Level::ForceWarn => false,
             Level::Deny | Level::Forbid => true,
         }
     }
-
-    pub fn get_expectation_id(&self) -> Option<LintExpectationId> {
-        match self {
-            Level::Expect(id) | Level::ForceWarn(Some(id)) => Some(*id),
-            _ => None,
-        }
-    }
 }
 
 /// Specification of a single lint.
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index 16f87ab79be..1c3222bbfeb 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -340,7 +340,7 @@ impl CStore {
         }
         let level = tcx
             .lint_level_at_node(lint::builtin::UNUSED_CRATE_DEPENDENCIES, rustc_hir::CRATE_HIR_ID)
-            .0;
+            .level;
         if level != lint::Level::Allow {
             let unused_externs =
                 self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::<Vec<_>>();
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index 776b081a463..3dc82ce9d18 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -330,14 +330,8 @@ provide! { tcx, def_id, other, cdata,
 
     visibility => { cdata.get_visibility(def_id.index) }
     adt_def => { cdata.get_adt_def(def_id.index, tcx) }
-    adt_destructor => {
-        let _ = cdata;
-        tcx.calculate_dtor(def_id, |_,_| Ok(()))
-    }
-    adt_async_destructor => {
-        let _ = cdata;
-        tcx.calculate_async_dtor(def_id, |_,_| Ok(()))
-    }
+    adt_destructor => { table }
+    adt_async_destructor => { table }
     associated_item_def_ids => {
         tcx.arena.alloc_from_iter(cdata.get_associated_item_or_field_def_ids(def_id.index))
     }
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 386d3d3156a..167122a9793 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1634,6 +1634,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 record!(self.tables.fn_sig[variant.def_id] <- fn_sig);
             }
         }
+
+        if let Some(destructor) = tcx.adt_destructor(local_def_id) {
+            record!(self.tables.adt_destructor[def_id] <- destructor);
+        }
+
+        if let Some(destructor) = tcx.adt_async_destructor(local_def_id) {
+            record!(self.tables.adt_async_destructor[def_id] <- destructor);
+        }
     }
 
     #[instrument(level = "debug", skip(self))]
@@ -1882,8 +1890,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         let is_proc_macro = self.tcx.crate_types().contains(&CrateType::ProcMacro);
         if is_proc_macro {
             let tcx = self.tcx;
-            let hir = tcx.hir();
-
             let proc_macro_decls_static = tcx.proc_macro_decls_static(()).unwrap().local_def_index;
             let stability = tcx.lookup_stability(CRATE_DEF_ID);
             let macros =
@@ -1916,7 +1922,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 let id = proc_macro;
                 let proc_macro = tcx.local_def_id_to_hir_id(proc_macro);
                 let mut name = tcx.hir_name(proc_macro);
-                let span = hir.span(proc_macro);
+                let span = tcx.hir_span(proc_macro);
                 // Proc-macros may have attributes like `#[allow_internal_unstable]`,
                 // so downstream crates need access to them.
                 let attrs = tcx.hir_attrs(proc_macro);
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index f4cf338ffb5..b1bc4b43ab4 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -452,6 +452,8 @@ define_tables! {
     fn_arg_names: Table<DefIndex, LazyArray<Option<Ident>>>,
     coroutine_kind: Table<DefIndex, hir::CoroutineKind>,
     coroutine_for_closure: Table<DefIndex, RawDefId>,
+    adt_destructor: Table<DefIndex, LazyValue<ty::Destructor>>,
+    adt_async_destructor: Table<DefIndex, LazyValue<ty::AsyncDestructor>>,
     coroutine_by_move_body_def_id: Table<DefIndex, RawDefId>,
     eval_static_initializer: Table<DefIndex, LazyValue<mir::interpret::ConstAllocation<'static>>>,
     trait_def: Table<DefIndex, LazyValue<ty::TraitDef>>,
diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs
index be34c7ef4bd..644cdac5d55 100644
--- a/compiler/rustc_middle/src/dep_graph/dep_node.rs
+++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs
@@ -89,6 +89,7 @@ rustc_query_append!(define_dep_nodes![
     /// We use this to create a forever-red node.
     [] fn Red() -> (),
     [] fn SideEffect() -> (),
+    [] fn AnonZeroDeps() -> (),
     [] fn TraitSelect() -> (),
     [] fn CompileCodegenUnit() -> (),
     [] fn CompileMonoItem() -> (),
diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs
index 739c0be1a91..931d67087ac 100644
--- a/compiler/rustc_middle/src/dep_graph/mod.rs
+++ b/compiler/rustc_middle/src/dep_graph/mod.rs
@@ -53,6 +53,7 @@ impl Deps for DepsType {
     const DEP_KIND_NULL: DepKind = dep_kinds::Null;
     const DEP_KIND_RED: DepKind = dep_kinds::Red;
     const DEP_KIND_SIDE_EFFECT: DepKind = dep_kinds::SideEffect;
+    const DEP_KIND_ANON_ZERO_DEPS: DepKind = dep_kinds::AnonZeroDeps;
     const DEP_KIND_MAX: u16 = dep_node::DEP_KIND_VARIANTS - 1;
 }
 
diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs
index 52f155a16b8..21ab06c98a7 100644
--- a/compiler/rustc_middle/src/hir/map.rs
+++ b/compiler/rustc_middle/src/hir/map.rs
@@ -22,6 +22,7 @@ use crate::ty::TyCtxt;
 // only serves as "namespace" for HIR-related methods, and can be
 // removed if all the methods are reasonably renamed and moved to tcx
 // (https://github.com/rust-lang/rust/pull/118256#issuecomment-1826442834).
+#[allow(unused)] // FIXME: temporary
 #[derive(Copy, Clone)]
 pub struct Map<'hir> {
     pub(super) tcx: TyCtxt<'hir>,
@@ -273,7 +274,7 @@ impl<'tcx> TyCtxt<'tcx> {
         self.hir_maybe_body_owned_by(id).unwrap_or_else(|| {
             let hir_id = self.local_def_id_to_hir_id(id);
             span_bug!(
-                self.hir().span(hir_id),
+                self.hir_span(hir_id),
                 "body_owned_by: {} has no associated body",
                 self.hir_id_to_string(hir_id)
             );
@@ -367,10 +368,6 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
-    pub fn hir_trait_impls(self, trait_did: DefId) -> &'tcx [LocalDefId] {
-        self.all_local_trait_impls(()).get(&trait_did).map_or(&[], |xs| &xs[..])
-    }
-
     /// Gets the attributes on the crate. This is preferable to
     /// invoking `krate.attrs` because it registers a tighter
     /// dep-graph access.
@@ -681,9 +678,8 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn hir_id_to_string(self, id: HirId) -> String {
         let path_str = |def_id: LocalDefId| self.def_path_str(def_id);
 
-        let span_str = || {
-            self.sess.source_map().span_to_snippet(Map { tcx: self }.span(id)).unwrap_or_default()
-        };
+        let span_str =
+            || self.sess.source_map().span_to_snippet(self.hir_span(id)).unwrap_or_default();
         let node_str = |prefix| format!("{id} ({prefix} `{}`)", span_str());
 
         match self.hir_node(id) {
@@ -879,12 +875,10 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn hir_attrs(self, id: HirId) -> &'tcx [Attribute] {
         self.hir_attr_map(id.owner).get(id.local_id)
     }
-}
 
-impl<'hir> Map<'hir> {
     /// Gets the span of the definition of the specified HIR node.
     /// This is used by `tcx.def_span`.
-    pub fn span(self, hir_id: HirId) -> Span {
+    pub fn hir_span(self, hir_id: HirId) -> Span {
         fn until_within(outer: Span, end: Span) -> Span {
             if let Some(end) = end.find_ancestor_inside(outer) {
                 outer.with_hi(end.hi())
@@ -904,7 +898,7 @@ impl<'hir> Map<'hir> {
             span
         }
 
-        let span = match self.tcx.hir_node(hir_id) {
+        let span = match self.hir_node(hir_id) {
             // Function-like.
             Node::Item(Item { kind: ItemKind::Fn { sig, .. }, span: outer_span, .. })
             | Node::TraitItem(TraitItem {
@@ -984,7 +978,7 @@ impl<'hir> Map<'hir> {
             Node::Variant(variant) => named_span(variant.span, variant.ident, None),
             Node::ImplItem(item) => named_span(item.span, item.ident, Some(item.generics)),
             Node::ForeignItem(item) => named_span(item.span, item.ident, None),
-            Node::Ctor(_) => return self.span(self.tcx.parent_hir_id(hir_id)),
+            Node::Ctor(_) => return self.hir_span(self.parent_hir_id(hir_id)),
             Node::Expr(Expr {
                 kind: ExprKind::Closure(Closure { fn_decl_span, .. }),
                 span,
@@ -993,16 +987,16 @@ impl<'hir> Map<'hir> {
                 // Ensure that the returned span has the item's SyntaxContext.
                 fn_decl_span.find_ancestor_inside(*span).unwrap_or(*span)
             }
-            _ => self.span_with_body(hir_id),
+            _ => self.hir_span_with_body(hir_id),
         };
-        debug_assert_eq!(span.ctxt(), self.span_with_body(hir_id).ctxt());
+        debug_assert_eq!(span.ctxt(), self.hir_span_with_body(hir_id).ctxt());
         span
     }
 
-    /// Like `hir.span()`, but includes the body of items
+    /// Like `hir_span()`, but includes the body of items
     /// (instead of just the item header)
-    pub fn span_with_body(self, hir_id: HirId) -> Span {
-        match self.tcx.hir_node(hir_id) {
+    pub fn hir_span_with_body(self, hir_id: HirId) -> Span {
+        match self.hir_node(hir_id) {
             Node::Param(param) => param.span,
             Node::Item(item) => item.span,
             Node::ForeignItem(foreign_item) => foreign_item.span,
@@ -1011,7 +1005,7 @@ impl<'hir> Map<'hir> {
             Node::Variant(variant) => variant.span,
             Node::Field(field) => field.span,
             Node::AnonConst(constant) => constant.span,
-            Node::ConstBlock(constant) => self.tcx.hir_body(constant.body).value.span,
+            Node::ConstBlock(constant) => self.hir_body(constant.body).value.span,
             Node::ConstArg(const_arg) => const_arg.span(),
             Node::Expr(expr) => expr.span,
             Node::ExprField(field) => field.span,
@@ -1031,7 +1025,7 @@ impl<'hir> Map<'hir> {
             Node::PatExpr(lit) => lit.span,
             Node::Arm(arm) => arm.span,
             Node::Block(block) => block.span,
-            Node::Ctor(..) => self.span_with_body(self.tcx.parent_hir_id(hir_id)),
+            Node::Ctor(..) => self.hir_span_with_body(self.parent_hir_id(hir_id)),
             Node::Lifetime(lifetime) => lifetime.ident.span,
             Node::GenericParam(param) => param.span,
             Node::Infer(i) => i.span,
@@ -1044,23 +1038,23 @@ impl<'hir> Map<'hir> {
         }
     }
 
-    pub fn span_if_local(self, id: DefId) -> Option<Span> {
-        id.is_local().then(|| self.tcx.def_span(id))
+    pub fn hir_span_if_local(self, id: DefId) -> Option<Span> {
+        id.is_local().then(|| self.def_span(id))
     }
 
-    pub fn res_span(self, res: Res) -> Option<Span> {
+    pub fn hir_res_span(self, res: Res) -> Option<Span> {
         match res {
             Res::Err => None,
-            Res::Local(id) => Some(self.span(id)),
-            res => self.span_if_local(res.opt_def_id()?),
+            Res::Local(id) => Some(self.hir_span(id)),
+            res => self.hir_span_if_local(res.opt_def_id()?),
         }
     }
 
     /// Returns the HirId of `N` in `struct Foo<const N: usize = { ... }>` when
     /// called with the HirId for the `{ ... }` anon const
-    pub fn opt_const_param_default_param_def_id(self, anon_const: HirId) -> Option<LocalDefId> {
-        let const_arg = self.tcx.parent_hir_id(anon_const);
-        match self.tcx.parent_hir_node(const_arg) {
+    pub fn hir_opt_const_param_default_param_def_id(self, anon_const: HirId) -> Option<LocalDefId> {
+        let const_arg = self.parent_hir_id(anon_const);
+        match self.parent_hir_node(const_arg) {
             Node::GenericParam(GenericParam {
                 def_id: param_id,
                 kind: GenericParamKind::Const { .. },
@@ -1070,7 +1064,7 @@ impl<'hir> Map<'hir> {
         }
     }
 
-    pub fn maybe_get_struct_pattern_shorthand_field(&self, expr: &Expr<'_>) -> Option<Symbol> {
+    pub fn hir_maybe_get_struct_pattern_shorthand_field(self, expr: &Expr<'_>) -> Option<Symbol> {
         let local = match expr {
             Expr {
                 kind:
@@ -1085,7 +1079,7 @@ impl<'hir> Map<'hir> {
             _ => None,
         }?;
 
-        match self.tcx.parent_hir_node(expr.hir_id) {
+        match self.parent_hir_node(expr.hir_id) {
             Node::ExprField(field) => {
                 if field.ident.name == local.name && field.is_shorthand {
                     return Some(local.name);
diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs
index 347bc5ea312..74369b6636c 100644
--- a/compiler/rustc_middle/src/hir/mod.rs
+++ b/compiler/rustc_middle/src/hir/mod.rs
@@ -210,13 +210,12 @@ pub fn provide(providers: &mut Providers) {
     providers.hir_attr_map = |tcx, id| {
         tcx.hir_crate(()).owners[id.def_id].as_owner().map_or(AttributeMap::EMPTY, |o| &o.attrs)
     };
-    providers.def_span = |tcx, def_id| tcx.hir().span(tcx.local_def_id_to_hir_id(def_id));
+    providers.def_span = |tcx, def_id| tcx.hir_span(tcx.local_def_id_to_hir_id(def_id));
     providers.def_ident_span = |tcx, def_id| {
         let hir_id = tcx.local_def_id_to_hir_id(def_id);
         tcx.hir_opt_ident_span(hir_id)
     };
     providers.fn_arg_names = |tcx, def_id| {
-        let hir = tcx.hir();
         if let Some(body_id) = tcx.hir_node_by_def_id(def_id).body_id() {
             tcx.arena.alloc_from_iter(tcx.hir_body_param_names(body_id))
         } else if let Node::TraitItem(&TraitItem {
@@ -231,13 +230,15 @@ pub fn provide(providers: &mut Providers) {
             idents
         } else {
             span_bug!(
-                hir.span(tcx.local_def_id_to_hir_id(def_id)),
+                tcx.hir_span(tcx.local_def_id_to_hir_id(def_id)),
                 "fn_arg_names: unexpected item {:?}",
                 def_id
             );
         }
     };
     providers.all_local_trait_impls = |tcx, ()| &tcx.resolutions(()).trait_impls;
+    providers.local_trait_impls =
+        |tcx, trait_id| tcx.resolutions(()).trait_impls.get(&trait_id).map_or(&[], |xs| &xs[..]);
     providers.expn_that_defined =
         |tcx, id| tcx.resolutions(()).expn_that_defined.get(&id).copied().unwrap_or(ExpnId::root());
     providers.in_scope_traits_map = |tcx, id| {
diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs
index 88bf17070b9..d5a408fdfa6 100644
--- a/compiler/rustc_middle/src/lint.rs
+++ b/compiler/rustc_middle/src/lint.rs
@@ -51,8 +51,13 @@ impl LintLevelSource {
     }
 }
 
-/// A tuple of a lint level and its source.
-pub type LevelAndSource = (Level, LintLevelSource);
+/// Convenience helper for moving things around together that frequently are paired
+#[derive(Copy, Clone, Debug, HashStable, Encodable, Decodable)]
+pub struct LevelAndSource {
+    pub level: Level,
+    pub lint_id: Option<LintExpectationId>,
+    pub src: LintLevelSource,
+}
 
 /// Return type for the `shallow_lint_levels_on` query.
 ///
@@ -69,14 +74,18 @@ pub struct ShallowLintLevelMap {
 ///
 /// The return of this function is suitable for diagnostics.
 pub fn reveal_actual_level(
-    level: Option<Level>,
+    level: Option<(Level, Option<LintExpectationId>)>,
     src: &mut LintLevelSource,
     sess: &Session,
     lint: LintId,
-    probe_for_lint_level: impl FnOnce(LintId) -> (Option<Level>, LintLevelSource),
-) -> Level {
+    probe_for_lint_level: impl FnOnce(
+        LintId,
+    )
+        -> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource),
+) -> (Level, Option<LintExpectationId>) {
     // If `level` is none then we actually assume the default level for this lint.
-    let mut level = level.unwrap_or_else(|| lint.lint.default_level(sess.edition()));
+    let (mut level, mut lint_id) =
+        level.unwrap_or_else(|| (lint.lint.default_level(sess.edition()), None));
 
     // If we're about to issue a warning, check at the last minute for any
     // directives against the warnings "lint". If, for example, there's an
@@ -88,16 +97,17 @@ pub fn reveal_actual_level(
     // future compatibility warning.
     if level == Level::Warn && lint != LintId::of(FORBIDDEN_LINT_GROUPS) {
         let (warnings_level, warnings_src) = probe_for_lint_level(LintId::of(builtin::WARNINGS));
-        if let Some(configured_warning_level) = warnings_level {
+        if let Some((configured_warning_level, configured_lint_id)) = warnings_level {
             if configured_warning_level != Level::Warn {
                 level = configured_warning_level;
+                lint_id = configured_lint_id;
                 *src = warnings_src;
             }
         }
     }
 
     // Ensure that we never exceed the `--cap-lints` argument unless the source is a --force-warn
-    level = if let LintLevelSource::CommandLine(_, Level::ForceWarn(_)) = src {
+    level = if let LintLevelSource::CommandLine(_, Level::ForceWarn) = src {
         level
     } else {
         cmp::min(level, sess.opts.lint_cap.unwrap_or(Level::Forbid))
@@ -108,7 +118,7 @@ pub fn reveal_actual_level(
         level = cmp::min(*driver_level, level);
     }
 
-    level
+    (level, lint_id)
 }
 
 impl ShallowLintLevelMap {
@@ -121,11 +131,11 @@ impl ShallowLintLevelMap {
         tcx: TyCtxt<'_>,
         id: LintId,
         start: HirId,
-    ) -> (Option<Level>, LintLevelSource) {
+    ) -> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource) {
         if let Some(map) = self.specs.get(&start.local_id)
-            && let Some(&(level, src)) = map.get(&id)
+            && let Some(&LevelAndSource { level, lint_id, src }) = map.get(&id)
         {
-            return (Some(level), src);
+            return (Some((level, lint_id)), src);
         }
 
         let mut owner = start.owner;
@@ -137,9 +147,9 @@ impl ShallowLintLevelMap {
                 specs = &tcx.shallow_lint_levels_on(owner).specs;
             }
             if let Some(map) = specs.get(&parent.local_id)
-                && let Some(&(level, src)) = map.get(&id)
+                && let Some(&LevelAndSource { level, lint_id, src }) = map.get(&id)
             {
-                return (Some(level), src);
+                return (Some((level, lint_id)), src);
             }
         }
 
@@ -153,18 +163,18 @@ impl ShallowLintLevelMap {
         tcx: TyCtxt<'_>,
         lint: LintId,
         cur: HirId,
-    ) -> (Level, LintLevelSource) {
+    ) -> LevelAndSource {
         let (level, mut src) = self.probe_for_lint_level(tcx, lint, cur);
-        let level = reveal_actual_level(level, &mut src, tcx.sess, lint, |lint| {
+        let (level, lint_id) = reveal_actual_level(level, &mut src, tcx.sess, lint, |lint| {
             self.probe_for_lint_level(tcx, lint, cur)
         });
-        (level, src)
+        LevelAndSource { level, lint_id, src }
     }
 }
 
 impl TyCtxt<'_> {
     /// Fetch and return the user-visible lint level for the given lint at the given HirId.
-    pub fn lint_level_at_node(self, lint: &'static Lint, id: HirId) -> (Level, LintLevelSource) {
+    pub fn lint_level_at_node(self, lint: &'static Lint, id: HirId) -> LevelAndSource {
         self.shallow_lint_levels_on(id.owner).lint_level_id_at_node(self, LintId::of(lint), id)
     }
 }
@@ -267,8 +277,7 @@ fn explain_lint_level_source(
 pub fn lint_level(
     sess: &Session,
     lint: &'static Lint,
-    level: Level,
-    src: LintLevelSource,
+    level: LevelAndSource,
     span: Option<MultiSpan>,
     decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
 ) {
@@ -278,11 +287,12 @@ pub fn lint_level(
     fn lint_level_impl(
         sess: &Session,
         lint: &'static Lint,
-        level: Level,
-        src: LintLevelSource,
+        level: LevelAndSource,
         span: Option<MultiSpan>,
         decorate: Box<dyn '_ + for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>)>,
     ) {
+        let LevelAndSource { level, lint_id, src } = level;
+
         // Check for future incompatibility lints and issue a stronger warning.
         let future_incompatible = lint.future_incompatible;
 
@@ -301,7 +311,7 @@ pub fn lint_level(
                     return;
                 }
             }
-            Level::Expect(expect_id) => {
+            Level::Expect => {
                 // This case is special as we actually allow the lint itself in this context, but
                 // we can't return early like in the case for `Level::Allow` because we still
                 // need the lint diagnostic to be emitted to `rustc_error::DiagCtxtInner`.
@@ -309,10 +319,9 @@ pub fn lint_level(
                 // We can also not mark the lint expectation as fulfilled here right away, as it
                 // can still be cancelled in the decorate function. All of this means that we simply
                 // create a `Diag` and continue as we would for warnings.
-                rustc_errors::Level::Expect(expect_id)
+                rustc_errors::Level::Expect
             }
-            Level::ForceWarn(Some(expect_id)) => rustc_errors::Level::ForceWarning(Some(expect_id)),
-            Level::ForceWarn(None) => rustc_errors::Level::ForceWarning(None),
+            Level::ForceWarn => rustc_errors::Level::ForceWarning,
             Level::Warn => rustc_errors::Level::Warning,
             Level::Deny | Level::Forbid => rustc_errors::Level::Error,
         };
@@ -320,6 +329,9 @@ pub fn lint_level(
         if let Some(span) = span {
             err.span(span);
         }
+        if let Some(lint_id) = lint_id {
+            err.lint_id(lint_id);
+        }
 
         // If this code originates in a foreign macro, aka something that this crate
         // did not itself author, then it's likely that there's nothing this crate
@@ -350,7 +362,7 @@ pub fn lint_level(
         // the compiler. It is therefore not necessary to add any information for the user.
         // This will therefore directly call the decorate function which will in turn emit
         // the diagnostic.
-        if let Level::Expect(_) = level {
+        if let Level::Expect = level {
             decorate(&mut err);
             err.emit();
             return;
@@ -419,5 +431,5 @@ pub fn lint_level(
         explain_lint_level_source(lint, level, src, &mut err);
         err.emit()
     }
-    lint_level_impl(sess, lint, level, src, span, Box::new(decorate))
+    lint_level_impl(sess, lint, level, span, Box::new(decorate))
 }
diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs
index ba31f775b65..92eab59dd02 100644
--- a/compiler/rustc_middle/src/middle/region.rs
+++ b/compiler/rustc_middle/src/middle/region.rs
@@ -175,7 +175,7 @@ impl Scope {
         let Some(hir_id) = self.hir_id(scope_tree) else {
             return DUMMY_SP;
         };
-        let span = tcx.hir().span(hir_id);
+        let span = tcx.hir_span(hir_id);
         if let ScopeData::Remainder(first_statement_index) = self.data {
             if let Node::Block(blk) = tcx.hir_node(hir_id) {
                 // Want span for scope starting after the
diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs
index ec128c8c478..9912e659b05 100644
--- a/compiler/rustc_middle/src/middle/stability.rs
+++ b/compiler/rustc_middle/src/middle/stability.rs
@@ -255,7 +255,7 @@ fn late_report_deprecation(
     // Calculating message for lint involves calling `self.def_path_str`,
     // which will by default invoke the expensive `visible_parent_map` query.
     // Skip all that work if the lint is allowed anyway.
-    if tcx.lint_level_at_node(lint, hir_id).0 == Level::Allow {
+    if tcx.lint_level_at_node(lint, hir_id).level == Level::Allow {
         return;
     }
 
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 6d6e6a1f185..707c8d04d55 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -1668,6 +1668,42 @@ pub enum BinOp {
     Offset,
 }
 
+// Assignment operators, e.g. `+=`. See comments on the corresponding variants
+// in `BinOp` for details.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
+pub enum AssignOp {
+    AddAssign,
+    SubAssign,
+    MulAssign,
+    DivAssign,
+    RemAssign,
+    BitXorAssign,
+    BitAndAssign,
+    BitOrAssign,
+    ShlAssign,
+    ShrAssign,
+}
+
+// Sometimes `BinOp` and `AssignOp` need the same treatment. The operations
+// covered by `AssignOp` are a subset of those covered by `BinOp`, so it makes
+// sense to convert `AssignOp` to `BinOp`.
+impl From<AssignOp> for BinOp {
+    fn from(op: AssignOp) -> BinOp {
+        match op {
+            AssignOp::AddAssign => BinOp::Add,
+            AssignOp::SubAssign => BinOp::Sub,
+            AssignOp::MulAssign => BinOp::Mul,
+            AssignOp::DivAssign => BinOp::Div,
+            AssignOp::RemAssign => BinOp::Rem,
+            AssignOp::BitXorAssign => BinOp::BitXor,
+            AssignOp::BitAndAssign => BinOp::BitAnd,
+            AssignOp::BitOrAssign => BinOp::BitOr,
+            AssignOp::ShlAssign => BinOp::Shl,
+            AssignOp::ShrAssign => BinOp::Shr,
+        }
+    }
+}
+
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
 #[cfg(target_pointer_width = "64")]
 mod size_asserts {
diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs
index c382bcd726f..9ed1f10455a 100644
--- a/compiler/rustc_middle/src/query/keys.rs
+++ b/compiler/rustc_middle/src/query/keys.rs
@@ -586,7 +586,7 @@ impl Key for HirId {
     type Cache<V> = DefaultCache<Self, V>;
 
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        tcx.hir().span(*self)
+        tcx.hir_span(*self)
     }
 
     #[inline(always)]
@@ -599,7 +599,7 @@ impl Key for (LocalDefId, HirId) {
     type Cache<V> = DefaultCache<Self, V>;
 
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        tcx.hir().span(self.1)
+        tcx.hir_span(self.1)
     }
 
     #[inline(always)]
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index d7ed703f4ae..698859c663b 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -612,6 +612,7 @@ rustc_queries! {
 
     query check_coroutine_obligations(key: LocalDefId) -> Result<(), ErrorGuaranteed> {
         desc { |tcx| "verify auto trait bounds for coroutine interior type `{}`", tcx.def_path_str(key) }
+        return_result_from_ensure_ok
     }
 
     /// MIR after our optimization passes have run. This is MIR that is ready
@@ -1033,13 +1034,12 @@ rustc_queries! {
     /// Unsafety-check this `LocalDefId`.
     query check_unsafety(key: LocalDefId) {
         desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) }
-        cache_on_disk_if { true }
     }
 
     /// Checks well-formedness of tail calls (`become f()`).
     query check_tail_calls(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> {
         desc { |tcx| "tail-call-checking `{}`", tcx.def_path_str(key) }
-        cache_on_disk_if { true }
+        return_result_from_ensure_ok
     }
 
     /// Returns the types assumed to be well formed while "inside" of the given item.
@@ -1308,7 +1308,7 @@ rustc_queries! {
 
     query check_match(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> {
         desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) }
-        cache_on_disk_if { true }
+        return_result_from_ensure_ok
     }
 
     /// Performs part of the privacy check and computes effective visibilities.
@@ -1502,6 +1502,11 @@ rustc_queries! {
         desc { "finding local trait impls" }
     }
 
+    /// Return all `impl` blocks of the given trait in the current crate.
+    query local_trait_impls(trait_id: DefId) -> &'tcx [LocalDefId] {
+        desc { "finding local trait impls of `{}`", tcx.def_path_str(trait_id) }
+    }
+
     /// Given a trait `trait_id`, return all known `impl` blocks.
     query trait_impls_of(trait_id: DefId) -> &'tcx ty::trait_def::TraitImpls {
         arena_cache
@@ -1602,7 +1607,6 @@ rustc_queries! {
     /// `Err(AlwaysRequiresDrop)` is returned.
     query adt_significant_drop_tys(def_id: DefId) -> Result<&'tcx ty::List<Ty<'tcx>>, AlwaysRequiresDrop> {
         desc { |tcx| "computing when `{}` has a significant destructor", tcx.def_path_str(def_id) }
-        cache_on_disk_if { false }
     }
 
     /// Returns a list of types which (a) have a potentially significant destructor
@@ -1624,7 +1628,6 @@ rustc_queries! {
     /// Otherwise, there is a risk of query cycles.
     query list_significant_drop_tys(ty: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> &'tcx ty::List<Ty<'tcx>> {
         desc { |tcx| "computing when `{}` has a significant destructor", ty.value }
-        cache_on_disk_if { false }
     }
 
     /// Computes the layout of a type. Note that this implicitly
@@ -2512,7 +2515,6 @@ rustc_queries! {
     /// monomorphized.
     query check_mono_item(key: ty::Instance<'tcx>) {
         desc { "monomorphization-time checking" }
-        cache_on_disk_if { true }
     }
 
     /// Builds the set of functions that should be skipped for the move-size check.
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index 6783bbf8bf4..8d373cb3b30 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -27,7 +27,7 @@ use tracing::instrument;
 
 use crate::middle::region;
 use crate::mir::interpret::AllocId;
-use crate::mir::{self, BinOp, BorrowKind, FakeReadCause, UnOp};
+use crate::mir::{self, AssignOp, BinOp, BorrowKind, FakeReadCause, UnOp};
 use crate::thir::visit::for_each_immediate_subpat;
 use crate::ty::adjustment::PointerCoercion;
 use crate::ty::layout::IntegerExt;
@@ -403,7 +403,7 @@ pub enum ExprKind<'tcx> {
     },
     /// A *non-overloaded* operation assignment, e.g. `lhs += rhs`.
     AssignOp {
-        op: BinOp,
+        op: AssignOp,
         lhs: ExprId,
         rhs: ExprId,
     },
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index cb245c0aec4..00fe5cb0c5d 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -236,7 +236,7 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
     }
 
     fn destructor(self, tcx: TyCtxt<'tcx>) -> Option<AdtDestructorKind> {
-        Some(match self.destructor(tcx)?.constness {
+        Some(match tcx.constness(self.destructor(tcx)?.did) {
             hir::Constness::Const => AdtDestructorKind::Const,
             hir::Constness::NotConst => AdtDestructorKind::NotConst,
         })
diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs
index 703b6ce9247..ff9096695d4 100644
--- a/compiler/rustc_middle/src/ty/closure.rs
+++ b/compiler/rustc_middle/src/ty/closure.rs
@@ -150,9 +150,9 @@ impl<'tcx> CapturedPlace<'tcx> {
     /// Return span pointing to use that resulted in selecting the captured path
     pub fn get_path_span(&self, tcx: TyCtxt<'tcx>) -> Span {
         if let Some(path_expr_id) = self.info.path_expr_id {
-            tcx.hir().span(path_expr_id)
+            tcx.hir_span(path_expr_id)
         } else if let Some(capture_kind_expr_id) = self.info.capture_kind_expr_id {
-            tcx.hir().span(capture_kind_expr_id)
+            tcx.hir_span(capture_kind_expr_id)
         } else {
             // Fallback on upvars mentioned if neither path or capture expr id is captured
 
@@ -166,9 +166,9 @@ impl<'tcx> CapturedPlace<'tcx> {
     /// Return span pointing to use that resulted in selecting the current capture kind
     pub fn get_capture_kind_span(&self, tcx: TyCtxt<'tcx>) -> Span {
         if let Some(capture_kind_expr_id) = self.info.capture_kind_expr_id {
-            tcx.hir().span(capture_kind_expr_id)
+            tcx.hir_span(capture_kind_expr_id)
         } else if let Some(path_expr_id) = self.info.path_expr_id {
-            tcx.hir().span(path_expr_id)
+            tcx.hir_span(path_expr_id)
         } else {
             // Fallback on upvars mentioned if neither path or capture expr id is captured
 
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 618a65a0186..13217ae58e9 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -446,6 +446,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.is_lang_item(def_id, trait_lang_item_to_lang_item(lang_item))
     }
 
+    fn is_default_trait(self, def_id: DefId) -> bool {
+        self.is_default_trait(def_id)
+    }
+
     fn as_lang_item(self, def_id: DefId) -> Option<TraitSolverLangItem> {
         lang_item_to_trait_lang_item(self.lang_items().from_def_id(def_id)?)
     }
@@ -1539,6 +1543,25 @@ impl<'tcx> TyCtxt<'tcx> {
         self.reserve_and_set_memory_dedup(alloc, salt)
     }
 
+    pub fn default_traits(self) -> &'static [rustc_hir::LangItem] {
+        match self.sess.opts.unstable_opts.experimental_default_bounds {
+            true => &[
+                LangItem::Sized,
+                LangItem::DefaultTrait1,
+                LangItem::DefaultTrait2,
+                LangItem::DefaultTrait3,
+                LangItem::DefaultTrait4,
+            ],
+            false => &[LangItem::Sized],
+        }
+    }
+
+    pub fn is_default_trait(self, def_id: DefId) -> bool {
+        self.default_traits()
+            .iter()
+            .any(|&default_trait| self.lang_items().get(default_trait) == Some(def_id))
+    }
+
     /// Returns a range of the start/end indices specified with the
     /// `rustc_layout_scalar_valid_range` attribute.
     // FIXME(eddyb) this is an awkward spot for this method, maybe move it?
@@ -3022,8 +3045,8 @@ impl<'tcx> TyCtxt<'tcx> {
         span: impl Into<MultiSpan>,
         decorator: impl for<'a> LintDiagnostic<'a, ()>,
     ) {
-        let (level, src) = self.lint_level_at_node(lint, hir_id);
-        lint_level(self.sess, lint, level, src, Some(span.into()), |lint| {
+        let level = self.lint_level_at_node(lint, hir_id);
+        lint_level(self.sess, lint, level, Some(span.into()), |lint| {
             decorator.decorate_lint(lint);
         })
     }
@@ -3040,8 +3063,8 @@ impl<'tcx> TyCtxt<'tcx> {
         span: impl Into<MultiSpan>,
         decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
     ) {
-        let (level, src) = self.lint_level_at_node(lint, hir_id);
-        lint_level(self.sess, lint, level, src, Some(span.into()), decorate);
+        let level = self.lint_level_at_node(lint, hir_id);
+        lint_level(self.sess, lint, level, Some(span.into()), decorate);
     }
 
     /// Find the crate root and the appropriate span where `use` and outer attributes can be
@@ -3108,8 +3131,8 @@ impl<'tcx> TyCtxt<'tcx> {
         id: HirId,
         decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
     ) {
-        let (level, src) = self.lint_level_at_node(lint, id);
-        lint_level(self.sess, lint, level, src, None, decorate);
+        let level = self.lint_level_at_node(lint, id);
+        lint_level(self.sess, lint, level, None, decorate);
     }
 
     pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx [TraitCandidate]> {
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index e3b3eccffb5..55ebd15248c 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -720,7 +720,7 @@ impl<'tcx> Instance<'tcx> {
             ty::TypingEnv::fully_monomorphized(),
             def_id,
             args,
-            ty.ty_adt_def().and_then(|adt| tcx.hir().span_if_local(adt.did())).unwrap_or(DUMMY_SP),
+            ty.ty_adt_def().and_then(|adt| tcx.hir_span_if_local(adt.did())).unwrap_or(DUMMY_SP),
         )
     }
 
@@ -732,7 +732,7 @@ impl<'tcx> Instance<'tcx> {
             ty::TypingEnv::fully_monomorphized(),
             def_id,
             args,
-            ty.ty_adt_def().and_then(|adt| tcx.hir().span_if_local(adt.did())).unwrap_or(DUMMY_SP),
+            ty.ty_adt_def().and_then(|adt| tcx.hir_span_if_local(adt.did())).unwrap_or(DUMMY_SP),
         )
     }
 
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 0ffaef82f1c..255d464d265 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1119,8 +1119,6 @@ pub struct PseudoCanonicalInput<'tcx, T> {
 pub struct Destructor {
     /// The `DefId` of the destructor method
     pub did: DefId,
-    /// The constness of the destructor method
-    pub constness: hir::Constness,
 }
 
 // FIXME: consider combining this definition with regular `Destructor`
diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs
index 19e2b574563..61b35b33a09 100644
--- a/compiler/rustc_middle/src/ty/parameterized.rs
+++ b/compiler/rustc_middle/src/ty/parameterized.rs
@@ -65,9 +65,11 @@ trivially_parameterized_over_tcx! {
     crate::middle::lib_features::FeatureStability,
     crate::middle::resolve_bound_vars::ObjectLifetimeDefault,
     crate::mir::ConstQualifs,
+    ty::AsyncDestructor,
     ty::AssocItemContainer,
     ty::Asyncness,
     ty::DeducedParamAttrs,
+    ty::Destructor,
     ty::Generics,
     ty::ImplPolarity,
     ty::ImplTraitInTraitData,
diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs
index 8fa1c569737..ea25ce65f77 100644
--- a/compiler/rustc_middle/src/ty/trait_def.rs
+++ b/compiler/rustc_middle/src/ty/trait_def.rs
@@ -129,21 +129,6 @@ impl<'tcx> TraitDef {
 }
 
 impl<'tcx> TyCtxt<'tcx> {
-    /// `trait_def_id` MUST BE the `DefId` of a trait.
-    pub fn for_each_impl<F: FnMut(DefId)>(self, trait_def_id: DefId, mut f: F) {
-        let impls = self.trait_impls_of(trait_def_id);
-
-        for &impl_def_id in impls.blanket_impls.iter() {
-            f(impl_def_id);
-        }
-
-        for v in impls.non_blanket_impls.values() {
-            for &impl_def_id in v {
-                f(impl_def_id);
-            }
-        }
-    }
-
     /// Iterate over every impl that could possibly match the self type `self_ty`.
     ///
     /// `trait_def_id` MUST BE the `DefId` of a trait.
@@ -235,7 +220,7 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> Trait
         }
     }
 
-    for &impl_def_id in tcx.hir_trait_impls(trait_id) {
+    for &impl_def_id in tcx.local_trait_impls(trait_id) {
         let impl_def_id = impl_def_id.to_def_id();
 
         let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity();
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index 06054e22e76..3d3f4e2773a 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -197,12 +197,6 @@ pub struct TypeckResults<'tcx> {
     /// formatting modified file tests/ui/coroutine/retain-resume-ref.rs
     pub coroutine_stalled_predicates: FxIndexSet<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>,
 
-    /// We sometimes treat byte string literals (which are of type `&[u8; N]`)
-    /// as `&[u8]`, depending on the pattern in which they are used.
-    /// This hashset records all instances where we behave
-    /// like this to allow `const_to_pat` to reliably handle this situation.
-    pub treat_byte_string_as_slice: ItemLocalSet,
-
     /// Contains the data for evaluating the effect of feature `capture_disjoint_fields`
     /// on closure size.
     pub closure_size_eval: LocalDefIdMap<ClosureSizeProfileData<'tcx>>,
@@ -237,7 +231,6 @@ impl<'tcx> TypeckResults<'tcx> {
             closure_fake_reads: Default::default(),
             rvalue_scopes: Default::default(),
             coroutine_stalled_predicates: Default::default(),
-            treat_byte_string_as_slice: Default::default(),
             closure_size_eval: Default::default(),
             offset_of_data: Default::default(),
         }
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index c0d4130336e..7743e202aae 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -389,55 +389,65 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Calculate the destructor of a given type.
     pub fn calculate_dtor(
         self,
-        adt_did: DefId,
-        validate: impl Fn(Self, DefId) -> Result<(), ErrorGuaranteed>,
+        adt_did: LocalDefId,
+        validate: impl Fn(Self, LocalDefId) -> Result<(), ErrorGuaranteed>,
     ) -> Option<ty::Destructor> {
         let drop_trait = self.lang_items().drop_trait()?;
         self.ensure_ok().coherent_trait(drop_trait).ok()?;
 
-        let ty = self.type_of(adt_did).instantiate_identity();
         let mut dtor_candidate = None;
-        self.for_each_relevant_impl(drop_trait, ty, |impl_did| {
+        // `Drop` impls can only be written in the same crate as the adt, and cannot be blanket impls
+        for &impl_did in self.local_trait_impls(drop_trait) {
+            let Some(adt_def) = self.type_of(impl_did).skip_binder().ty_adt_def() else { continue };
+            if adt_def.did() != adt_did.to_def_id() {
+                continue;
+            }
+
             if validate(self, impl_did).is_err() {
                 // Already `ErrorGuaranteed`, no need to delay a span bug here.
-                return;
+                continue;
             }
 
             let Some(item_id) = self.associated_item_def_ids(impl_did).first() else {
                 self.dcx()
                     .span_delayed_bug(self.def_span(impl_did), "Drop impl without drop function");
-                return;
+                continue;
             };
 
-            if let Some((old_item_id, _)) = dtor_candidate {
+            if let Some(old_item_id) = dtor_candidate {
                 self.dcx()
                     .struct_span_err(self.def_span(item_id), "multiple drop impls found")
                     .with_span_note(self.def_span(old_item_id), "other impl here")
                     .delay_as_bug();
             }
 
-            dtor_candidate = Some((*item_id, self.impl_trait_header(impl_did).unwrap().constness));
-        });
+            dtor_candidate = Some(*item_id);
+        }
 
-        let (did, constness) = dtor_candidate?;
-        Some(ty::Destructor { did, constness })
+        let did = dtor_candidate?;
+        Some(ty::Destructor { did })
     }
 
     /// Calculate the async destructor of a given type.
     pub fn calculate_async_dtor(
         self,
-        adt_did: DefId,
-        validate: impl Fn(Self, DefId) -> Result<(), ErrorGuaranteed>,
+        adt_did: LocalDefId,
+        validate: impl Fn(Self, LocalDefId) -> Result<(), ErrorGuaranteed>,
     ) -> Option<ty::AsyncDestructor> {
         let async_drop_trait = self.lang_items().async_drop_trait()?;
         self.ensure_ok().coherent_trait(async_drop_trait).ok()?;
 
-        let ty = self.type_of(adt_did).instantiate_identity();
         let mut dtor_candidate = None;
-        self.for_each_relevant_impl(async_drop_trait, ty, |impl_did| {
+        // `AsyncDrop` impls can only be written in the same crate as the adt, and cannot be blanket impls
+        for &impl_did in self.local_trait_impls(async_drop_trait) {
+            let Some(adt_def) = self.type_of(impl_did).skip_binder().ty_adt_def() else { continue };
+            if adt_def.did() != adt_did.to_def_id() {
+                continue;
+            }
+
             if validate(self, impl_did).is_err() {
                 // Already `ErrorGuaranteed`, no need to delay a span bug here.
-                return;
+                continue;
             }
 
             let [future, ctor] = self.associated_item_def_ids(impl_did) else {
@@ -445,7 +455,7 @@ impl<'tcx> TyCtxt<'tcx> {
                     self.def_span(impl_did),
                     "AsyncDrop impl without async_drop function or Dropper type",
                 );
-                return;
+                continue;
             };
 
             if let Some((_, _, old_impl_did)) = dtor_candidate {
@@ -456,7 +466,7 @@ impl<'tcx> TyCtxt<'tcx> {
             }
 
             dtor_candidate = Some((*future, *ctor, impl_did));
-        });
+        }
 
         let (future, ctor, _) = dtor_candidate?;
         Some(ty::AsyncDestructor { future, ctor })
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 50ca924baf9..fbe53081156 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs
@@ -159,7 +159,7 @@ fn find_capture_matching_projections<'a, 'tcx>(
 ) -> Option<(usize, &'a Capture<'tcx>)> {
     let hir_projections = convert_to_hir_projections_and_truncate_for_capture(projections);
 
-    upvars.get_by_key_enumerated(var_hir_id.0).find(|(_, capture)| {
+    upvars.get_by_key_enumerated(var_hir_id.0.local_id).find(|(_, capture)| {
         let possible_ancestor_proj_kinds: Vec<_> =
             capture.captured_place.place.projections.iter().map(|proj| proj.kind).collect();
         is_ancestor_or_same_capture(&possible_ancestor_proj_kinds, &hir_projections)
@@ -258,7 +258,7 @@ impl<'tcx> PlaceBuilder<'tcx> {
                 self.projection
             ),
             PlaceBase::Upvar { var_hir_id, closure_def_id: _ } => span_bug!(
-                cx.tcx.hir().span(var_hir_id.0),
+                cx.tcx.hir_span(var_hir_id.0),
                 "could not resolve upvar: {var_hir_id:?} + {:?}",
                 self.projection
             ),
diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
index 97d34b85f50..f9791776f71 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
@@ -416,7 +416,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     if let Some(mir_place) = place_builder.try_to_place(this) {
                         this.cfg.push_fake_read(
                             block,
-                            this.source_info(this.tcx.hir().span(*hir_id)),
+                            this.source_info(this.tcx.hir_span(*hir_id)),
                             *cause,
                             mir_place,
                         );
diff --git a/compiler/rustc_mir_build/src/builder/expr/stmt.rs b/compiler/rustc_mir_build/src/builder/expr/stmt.rs
index 7f8a0a34c31..2dff26f02f3 100644
--- a/compiler/rustc_mir_build/src/builder/expr/stmt.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/stmt.rs
@@ -78,8 +78,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 // because AssignOp is only legal for Copy types
                 // (overloaded ops should be desugared into a call).
                 let result = unpack!(
-                    block =
-                        this.build_binary_op(block, op, expr_span, lhs_ty, Operand::Copy(lhs), rhs)
+                    block = this.build_binary_op(
+                        block,
+                        op.into(),
+                        expr_span,
+                        lhs_ty,
+                        Operand::Copy(lhs),
+                        rhs
+                    )
                 );
                 this.cfg.push_assign(block, source_info, lhs, result);
 
diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs
index c8b69a6ec62..8ca9ab58e45 100644
--- a/compiler/rustc_mir_build/src/builder/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/mod.rs
@@ -13,7 +13,7 @@ use rustc_data_structures::sorted_map::SortedIndexMultiMap;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Node};
+use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node};
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_index::{Idx, IndexSlice, IndexVec};
 use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
@@ -48,11 +48,11 @@ pub(crate) fn closure_saved_names_of_captured_variables<'tcx>(
 /// this directly; instead use the cached version via `mir_built`.
 pub fn build_mir<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Body<'tcx> {
     tcx.ensure_done().thir_abstract_const(def);
-    if let Err(e) = tcx.check_match(def) {
+    if let Err(e) = tcx.ensure_ok().check_match(def) {
         return construct_error(tcx, def, e);
     }
 
-    if let Err(err) = tcx.check_tail_calls(def) {
+    if let Err(err) = tcx.ensure_ok().check_tail_calls(def) {
         return construct_error(tcx, def, err);
     }
 
@@ -221,7 +221,7 @@ struct Builder<'a, 'tcx> {
     coverage_info: Option<coverageinfo::CoverageInfoBuilder>,
 }
 
-type CaptureMap<'tcx> = SortedIndexMultiMap<usize, HirId, Capture<'tcx>>;
+type CaptureMap<'tcx> = SortedIndexMultiMap<usize, ItemLocalId, Capture<'tcx>>;
 
 #[derive(Debug)]
 struct Capture<'tcx> {
@@ -457,7 +457,7 @@ fn construct_fn<'tcx>(
 
     // Figure out what primary body this item has.
     let body = tcx.hir_body_owned_by(fn_def);
-    let span_with_body = tcx.hir().span_with_body(fn_id);
+    let span_with_body = tcx.hir_span_with_body(fn_id);
     let return_ty_span = tcx
         .hir_fn_decl_by_hir_id(fn_id)
         .unwrap_or_else(|| span_bug!(span, "can't build MIR for {:?}", fn_def))
@@ -853,6 +853,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let capture_tys = upvar_args.upvar_tys();
 
         let tcx = self.tcx;
+        let mut upvar_owner = None;
         self.upvars = tcx
             .closure_captures(self.def_id)
             .iter()
@@ -866,6 +867,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
                     _ => bug!("Expected an upvar"),
                 };
+                let upvar_base = upvar_owner.get_or_insert(var_id.owner);
+                assert_eq!(*upvar_base, var_id.owner);
+                let var_id = var_id.local_id;
 
                 let mutability = captured_place.mutability;
 
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index 7f2e7d5ca83..6fb9974fc8e 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -195,7 +195,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
 
     /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
     fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
-        self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow
+        self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).level == Level::Allow
     }
 
     /// Handle closures/coroutines/inline-consts, which is unsafecked with their parent body.
@@ -292,8 +292,10 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                 });
             }
             BlockSafety::ExplicitUnsafe(hir_id) => {
-                let used =
-                    matches!(self.tcx.lint_level_at_node(UNUSED_UNSAFE, hir_id), (Level::Allow, _));
+                let used = matches!(
+                    self.tcx.lint_level_at_node(UNUSED_UNSAFE, hir_id).level,
+                    Level::Allow
+                );
                 self.in_safety_context(
                     SafetyContext::UnsafeBlock {
                         span: block.span,
@@ -943,7 +945,7 @@ impl UnsafeOpKind {
             }
         });
         let unsafe_not_inherited_note = if let Some((id, _)) = note_non_inherited {
-            let span = tcx.hir().span(id);
+            let span = tcx.hir_span(id);
             let span = tcx.sess.source_map().guess_head_span(span);
             Some(UnsafeNotInheritedNote { span })
         } else {
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index b8af77245f2..31e22e69111 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -9,7 +9,7 @@ use rustc_middle::hir::place::{
     Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind,
 };
 use rustc_middle::middle::region;
-use rustc_middle::mir::{self, BinOp, BorrowKind, UnOp};
+use rustc_middle::mir::{self, AssignOp, BinOp, BorrowKind, UnOp};
 use rustc_middle::thir::*;
 use rustc_middle::ty::adjustment::{
     Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCoercion,
@@ -489,7 +489,7 @@ impl<'tcx> ThirBuildCx<'tcx> {
                     self.overloaded_operator(expr, Box::new([lhs, rhs]))
                 } else {
                     ExprKind::AssignOp {
-                        op: bin_op(op.node),
+                        op: assign_op(op.node),
                         lhs: self.mirror_expr(lhs),
                         rhs: self.mirror_expr(rhs),
                     }
@@ -1347,3 +1347,18 @@ fn bin_op(op: hir::BinOpKind) -> BinOp {
         _ => bug!("no equivalent for ast binop {:?}", op),
     }
 }
+
+fn assign_op(op: hir::AssignOpKind) -> AssignOp {
+    match op {
+        hir::AssignOpKind::AddAssign => AssignOp::AddAssign,
+        hir::AssignOpKind::SubAssign => AssignOp::SubAssign,
+        hir::AssignOpKind::MulAssign => AssignOp::MulAssign,
+        hir::AssignOpKind::DivAssign => AssignOp::DivAssign,
+        hir::AssignOpKind::RemAssign => AssignOp::RemAssign,
+        hir::AssignOpKind::BitXorAssign => AssignOp::BitXorAssign,
+        hir::AssignOpKind::BitAndAssign => AssignOp::BitAndAssign,
+        hir::AssignOpKind::BitOrAssign => AssignOp::BitOrAssign,
+        hir::AssignOpKind::ShlAssign => AssignOp::ShlAssign,
+        hir::AssignOpKind::ShrAssign => AssignOp::ShrAssign,
+    }
+}
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 095d3e75da1..9f5e2c06b22 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -1025,7 +1025,7 @@ fn find_fallback_pattern_typo<'tcx>(
     pat: &Pat<'tcx>,
     lint: &mut UnreachablePattern<'_>,
 ) {
-    if let (Level::Allow, _) = cx.tcx.lint_level_at_node(UNREACHABLE_PATTERNS, hir_id) {
+    if let Level::Allow = cx.tcx.lint_level_at_node(UNREACHABLE_PATTERNS, hir_id).level {
         // This is because we use `with_no_trimmed_paths` later, so if we never emit the lint we'd
         // ICE. At the same time, we don't really need to do all of this if we won't emit anything.
         return;
@@ -1174,7 +1174,7 @@ fn report_arm_reachability<'p, 'tcx>(
     for (arm, is_useful) in report.arm_usefulness.iter() {
         if let Usefulness::Redundant(explanation) = is_useful {
             let hir_id = arm.arm_data;
-            let arm_span = cx.tcx.hir().span(hir_id);
+            let arm_span = cx.tcx.hir_span(hir_id);
             let whole_arm_span = if is_match_arm {
                 // If the arm is followed by a comma, extend the span to include it.
                 let with_whitespace = sm.span_extend_while_whitespace(arm_span);
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 667d59d858e..372453688d2 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
@@ -58,25 +58,13 @@ struct ConstToPat<'tcx> {
     span: Span,
     id: hir::HirId,
 
-    treat_byte_string_as_slice: bool,
-
     c: ty::Const<'tcx>,
 }
 
 impl<'tcx> ConstToPat<'tcx> {
     fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self {
         trace!(?pat_ctxt.typeck_results.hir_owner);
-        ConstToPat {
-            tcx: pat_ctxt.tcx,
-            typing_env: pat_ctxt.typing_env,
-            span,
-            id,
-            treat_byte_string_as_slice: pat_ctxt
-                .typeck_results
-                .treat_byte_string_as_slice
-                .contains(&id.local_id),
-            c,
-        }
+        ConstToPat { tcx: pat_ctxt.tcx, typing_env: pat_ctxt.typing_env, span, id, c }
     }
 
     fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
@@ -108,8 +96,6 @@ impl<'tcx> ConstToPat<'tcx> {
         uv: ty::UnevaluatedConst<'tcx>,
         ty: Ty<'tcx>,
     ) -> Box<Pat<'tcx>> {
-        trace!(self.treat_byte_string_as_slice);
-
         // It's not *technically* correct to be revealing opaque types here as borrowcheck has
         // not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even
         // during typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821).
@@ -307,21 +293,8 @@ impl<'tcx> ConstToPat<'tcx> {
                             ty,
                         );
                     } else {
-                        // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
-                        // matching against references, you can only use byte string literals.
-                        // The typechecker has a special case for byte string literals, by treating them
-                        // as slices. This means we turn `&[T; N]` constants into slice patterns, which
-                        // has no negative effects on pattern matching, even if we're actually matching on
-                        // arrays.
-                        let pointee_ty = match *pointee_ty.kind() {
-                            ty::Array(elem_ty, _) if self.treat_byte_string_as_slice => {
-                                Ty::new_slice(tcx, elem_ty)
-                            }
-                            _ => *pointee_ty,
-                        };
                         // References have the same valtree representation as their pointee.
-                        let subpattern = self.valtree_to_pat(cv, pointee_ty);
-                        PatKind::Deref { subpattern }
+                        PatKind::Deref { subpattern: self.valtree_to_pat(cv, *pointee_ty) }
                     }
                 }
             },
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 4bfeab44bf4..d20e051548b 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -11,7 +11,7 @@ use rustc_abi::{FieldIdx, Integer};
 use rustc_errors::codes::*;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::pat_util::EnumerateAndAdjustIterator;
-use rustc_hir::{self as hir, RangeEnd};
+use rustc_hir::{self as hir, LangItem, RangeEnd};
 use rustc_index::Idx;
 use rustc_middle::mir::interpret::LitToConstInput;
 use rustc_middle::thir::{
@@ -130,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_pat_expr(expr);
+        let mut kind: PatKind<'tcx> = self.lower_pat_expr(expr, None);
 
         // Unpeel any ascription or inline-const wrapper nodes.
         loop {
@@ -294,7 +294,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
 
             hir::PatKind::Never => PatKind::Never,
 
-            hir::PatKind::Expr(value) => self.lower_pat_expr(value),
+            hir::PatKind::Expr(value) => self.lower_pat_expr(value, Some(ty)),
 
             hir::PatKind::Range(ref lo_expr, ref hi_expr, end) => {
                 let (lo_expr, hi_expr) = (lo_expr.as_deref(), hi_expr.as_deref());
@@ -630,7 +630,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
     /// - 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> {
+    fn lower_pat_expr(
+        &mut self,
+        expr: &'tcx hir::PatExpr<'tcx>,
+        pat_ty: Option<Ty<'tcx>>,
+    ) -> PatKind<'tcx> {
         let (lit, neg) = match &expr.kind {
             hir::PatExprKind::Path(qpath) => {
                 return self.lower_path(qpath, expr.hir_id, expr.span).kind;
@@ -641,7 +645,31 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             hir::PatExprKind::Lit { lit, negated } => (lit, *negated),
         };
 
-        let ct_ty = self.typeck_results.node_type(expr.hir_id);
+        // We handle byte string literal patterns by using the pattern's type instead of the
+        // literal's type in `const_to_pat`: if the literal `b"..."` matches on a slice reference,
+        // the pattern's type will be `&[u8]` whereas the literal's type is `&[u8; 3]`; using the
+        // pattern's type means we'll properly translate it to a slice reference pattern. This works
+        // because slices and arrays have the same valtree representation.
+        // HACK: As an exception, use the literal's type if `pat_ty` is `String`; this can happen if
+        // `string_deref_patterns` is enabled. There's a special case for that when lowering to MIR.
+        // FIXME(deref_patterns): This hack won't be necessary once `string_deref_patterns` is
+        // superseded by a more general implementation of deref patterns.
+        let ct_ty = match pat_ty {
+            Some(pat_ty)
+                if let ty::Adt(def, _) = *pat_ty.kind()
+                    && self.tcx.is_lang_item(def.did(), LangItem::String) =>
+            {
+                if !self.tcx.features().string_deref_patterns() {
+                    span_bug!(
+                        expr.span,
+                        "matching on `String` went through without enabling string_deref_patterns"
+                    );
+                }
+                self.typeck_results.node_type(expr.hir_id)
+            }
+            Some(pat_ty) => pat_ty,
+            None => self.typeck_results.node_type(expr.hir_id),
+        };
         let lit_input = LitToConstInput { lit: &lit.node, ty: ct_ty, neg };
         let constant = self.tcx.at(expr.span).lit_to_const(lit_input);
         self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind
diff --git a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs
index ceea72c6755..375db17fb73 100644
--- a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs
+++ b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs
@@ -53,9 +53,13 @@ impl<'tcx> ConstMutationChecker<'_, 'tcx> {
         //
         //     #[const_mutation_allowed]
         //     pub const LOG: Log = Log { msg: "" };
-        match self.tcx.calculate_dtor(def_id, |_, _| Ok(())) {
-            Some(_) => None,
-            None => Some(def_id),
+        // FIXME: this should not be checking for `Drop` impls,
+        // but whether it or any field has a Drop impl (`needs_drop`)
+        // as fields' Drop impls may make this observable, too.
+        match self.tcx.type_of(def_id).skip_binder().ty_adt_def().map(|adt| adt.has_dtor(self.tcx))
+        {
+            Some(true) => None,
+            Some(false) | None => Some(def_id),
         }
     }
 
diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs
index d83c0d40a7e..73bd2d0705e 100644
--- a/compiler/rustc_mir_transform/src/coverage/mappings.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs
@@ -96,7 +96,7 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
         }
     } else {
         // Extract coverage spans from MIR statements/terminators as normal.
-        extract_refined_covspans(mir_body, hir_info, graph, &mut code_mappings);
+        extract_refined_covspans(tcx, mir_body, hir_info, graph, &mut code_mappings);
     }
 
     branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, graph));
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index 8befe9c5d8d..f57a158e3e4 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -1,7 +1,9 @@
 use std::collections::VecDeque;
+use std::iter;
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::mir;
+use rustc_middle::ty::TyCtxt;
 use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span};
 use tracing::{debug, debug_span, instrument};
 
@@ -11,8 +13,9 @@ use crate::coverage::{ExtractedHirInfo, mappings, unexpand};
 
 mod from_mir;
 
-pub(super) fn extract_refined_covspans(
-    mir_body: &mir::Body<'_>,
+pub(super) fn extract_refined_covspans<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    mir_body: &mir::Body<'tcx>,
     hir_info: &ExtractedHirInfo,
     graph: &CoverageGraph,
     code_mappings: &mut impl Extend<mappings::CodeMapping>,
@@ -50,7 +53,7 @@ pub(super) fn extract_refined_covspans(
     // First, perform the passes that need macro information.
     covspans.sort_by(|a, b| graph.cmp_in_dominator_order(a.bcb, b.bcb));
     remove_unwanted_expansion_spans(&mut covspans);
-    split_visible_macro_spans(&mut covspans);
+    shrink_visible_macro_spans(tcx, &mut covspans);
 
     // We no longer need the extra information in `SpanFromMir`, so convert to `Covspan`.
     let mut covspans = covspans.into_iter().map(SpanFromMir::into_covspan).collect::<Vec<_>>();
@@ -83,9 +86,7 @@ pub(super) fn extract_refined_covspans(
     // Split the covspans into separate buckets that don't overlap any holes.
     let buckets = divide_spans_into_buckets(covspans, &holes);
 
-    for mut covspans in buckets {
-        // Make sure each individual bucket is internally sorted.
-        covspans.sort_by(compare_covspans);
+    for covspans in buckets {
         let _span = debug_span!("processing bucket", ?covspans).entered();
 
         let mut covspans = remove_unwanted_overlapping_spans(covspans);
@@ -129,82 +130,50 @@ fn remove_unwanted_expansion_spans(covspans: &mut Vec<SpanFromMir>) {
 }
 
 /// When a span corresponds to a macro invocation that is visible from the
-/// function body, split it into two parts. The first part covers just the
-/// macro name plus `!`, and the second part covers the rest of the macro
-/// invocation. This seems to give better results for code that uses macros.
-fn split_visible_macro_spans(covspans: &mut Vec<SpanFromMir>) {
-    let mut extra_spans = vec![];
-
-    covspans.retain(|covspan| {
-        let Some(ExpnKind::Macro(MacroKind::Bang, visible_macro)) = covspan.expn_kind else {
-            return true;
-        };
-
-        let split_len = visible_macro.as_str().len() as u32 + 1;
-        let (before, after) = covspan.span.split_at(split_len);
-        if !covspan.span.contains(before) || !covspan.span.contains(after) {
-            // Something is unexpectedly wrong with the split point.
-            // The debug assertion in `split_at` will have already caught this,
-            // but in release builds it's safer to do nothing and maybe get a
-            // bug report for unexpected coverage, rather than risk an ICE.
-            return true;
+/// function body, truncate it to just the macro name plus `!`.
+/// This seems to give better results for code that uses macros.
+fn shrink_visible_macro_spans(tcx: TyCtxt<'_>, covspans: &mut Vec<SpanFromMir>) {
+    let source_map = tcx.sess.source_map();
+
+    for covspan in covspans {
+        if matches!(covspan.expn_kind, Some(ExpnKind::Macro(MacroKind::Bang, _))) {
+            covspan.span = source_map.span_through_char(covspan.span, '!');
         }
-
-        extra_spans.push(SpanFromMir::new(before, covspan.expn_kind.clone(), covspan.bcb));
-        extra_spans.push(SpanFromMir::new(after, covspan.expn_kind.clone(), covspan.bcb));
-        false // Discard the original covspan that we just split.
-    });
-
-    // The newly-split spans are added at the end, so any previous sorting
-    // is not preserved.
-    covspans.extend(extra_spans);
+    }
 }
 
 /// Uses the holes to divide the given covspans into buckets, such that:
-/// - No span in any hole overlaps a bucket (truncating the spans if necessary).
+/// - No span in any hole overlaps a bucket (discarding spans if necessary).
 /// - The spans in each bucket are strictly after all spans in previous buckets,
 ///   and strictly before all spans in subsequent buckets.
 ///
-/// The resulting buckets are sorted relative to each other, but might not be
-/// internally sorted.
+/// The lists of covspans and holes must be sorted.
+/// The resulting buckets are sorted relative to each other, and each bucket's
+/// contents are sorted.
 #[instrument(level = "debug")]
 fn divide_spans_into_buckets(input_covspans: Vec<Covspan>, holes: &[Hole]) -> Vec<Vec<Covspan>> {
     debug_assert!(input_covspans.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));
     debug_assert!(holes.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));
 
-    // Now we're ready to start carving holes out of the initial coverage spans,
-    // and grouping them in buckets separated by the holes.
+    // Now we're ready to start grouping spans into buckets separated by holes.
 
     let mut input_covspans = VecDeque::from(input_covspans);
-    let mut fragments = vec![];
 
     // For each hole:
     // - Identify the spans that are entirely or partly before the hole.
-    // - Put those spans in a corresponding bucket, truncated to the start of the hole.
-    // - If one of those spans also extends after the hole, put the rest of it
-    //   in a "fragments" vector that is processed by the next hole.
+    // - Discard any that overlap with the hole.
+    // - Add the remaining identified spans to the corresponding bucket.
     let mut buckets = (0..holes.len()).map(|_| vec![]).collect::<Vec<_>>();
     for (hole, bucket) in holes.iter().zip(&mut buckets) {
-        let fragments_from_prev = std::mem::take(&mut fragments);
-
-        // Only inspect spans that precede or overlap this hole,
-        // leaving the rest to be inspected by later holes.
-        // (This relies on the spans and holes both being sorted.)
-        let relevant_input_covspans =
-            drain_front_while(&mut input_covspans, |c| c.span.lo() < hole.span.hi());
-
-        for covspan in fragments_from_prev.into_iter().chain(relevant_input_covspans) {
-            let (before, after) = covspan.split_around_hole_span(hole.span);
-            bucket.extend(before);
-            fragments.extend(after);
-        }
+        bucket.extend(
+            drain_front_while(&mut input_covspans, |c| c.span.lo() < hole.span.hi())
+                .filter(|c| !c.span.overlaps(hole.span)),
+        );
     }
 
-    // After finding the spans before each hole, any remaining fragments/spans
-    // form their own final bucket, after the final hole.
+    // Any remaining spans form their own final bucket, after the final hole.
     // (If there were no holes, this will just be all of the initial spans.)
-    fragments.extend(input_covspans);
-    buckets.push(fragments);
+    buckets.push(Vec::from(input_covspans));
 
     buckets
 }
@@ -215,7 +184,7 @@ fn drain_front_while<'a, T>(
     queue: &'a mut VecDeque<T>,
     mut pred_fn: impl FnMut(&T) -> bool,
 ) -> impl Iterator<Item = T> {
-    std::iter::from_fn(move || if pred_fn(queue.front()?) { queue.pop_front() } else { None })
+    iter::from_fn(move || queue.pop_front_if(|x| pred_fn(x)))
 }
 
 /// Takes one of the buckets of (sorted) spans extracted from MIR, and "refines"
@@ -258,22 +227,6 @@ struct Covspan {
 }
 
 impl Covspan {
-    /// Splits this covspan into 0-2 parts:
-    /// - The part that is strictly before the hole span, if any.
-    /// - The part that is strictly after the hole span, if any.
-    fn split_around_hole_span(&self, hole_span: Span) -> (Option<Self>, Option<Self>) {
-        let before = try {
-            let span = self.span.trim_end(hole_span)?;
-            Self { span, ..*self }
-        };
-        let after = try {
-            let span = self.span.trim_start(hole_span)?;
-            Self { span, ..*self }
-        };
-
-        (before, after)
-    }
-
     /// If `self` and `other` can be merged (i.e. they have the same BCB),
     /// mutates `self.span` to also include `other.span` and returns true.
     ///
diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
index 1faa2171c0b..804cd8ab3f7 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
@@ -120,22 +120,20 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
         // an `if condition { block }` has a span that includes the executed block, if true,
         // but for coverage, the code region executed, up to *and* through the SwitchInt,
         // actually stops before the if's block.)
-        TerminatorKind::Unreachable // Unreachable blocks are not connected to the MIR CFG
+        TerminatorKind::Unreachable
         | TerminatorKind::Assert { .. }
         | TerminatorKind::Drop { .. }
         | TerminatorKind::SwitchInt { .. }
-        // For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`.
         | TerminatorKind::FalseEdge { .. }
         | TerminatorKind::Goto { .. } => None,
 
         // Call `func` operand can have a more specific span when part of a chain of calls
-        TerminatorKind::Call { ref func, .. }
-        | TerminatorKind::TailCall { ref func, .. } => {
+        TerminatorKind::Call { ref func, .. } | TerminatorKind::TailCall { ref func, .. } => {
             let mut span = terminator.source_info.span;
-            if let mir::Operand::Constant(box constant) = func {
-                if constant.span.lo() > span.lo() {
-                    span = span.with_lo(constant.span.lo());
-                }
+            if let mir::Operand::Constant(constant) = func
+                && span.contains(constant.span)
+            {
+                span = constant.span;
             }
             Some(span)
         }
@@ -147,9 +145,7 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
         | TerminatorKind::Yield { .. }
         | TerminatorKind::CoroutineDrop
         | TerminatorKind::FalseUnwind { .. }
-        | TerminatorKind::InlineAsm { .. } => {
-            Some(terminator.source_info.span)
-        }
+        | TerminatorKind::InlineAsm { .. } => Some(terminator.source_info.span),
     }
 }
 
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 0a54c780f31..68bc0ffce6b 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -3,14 +3,16 @@
 //! MIR may contain repeated and/or redundant computations. The objective of this pass is to detect
 //! such redundancies and re-use the already-computed result when possible.
 //!
-//! In a first pass, we compute a symbolic representation of values that are assigned to SSA
-//! locals. This symbolic representation is defined by the `Value` enum. Each produced instance of
-//! `Value` is interned as a `VnIndex`, which allows us to cheaply compute identical values.
-//!
 //! From those assignments, we construct a mapping `VnIndex -> Vec<(Local, Location)>` of available
 //! values, the locals in which they are stored, and the assignment location.
 //!
-//! In a second pass, we traverse all (non SSA) assignments `x = rvalue` and operands. For each
+//! We traverse all assignments `x = rvalue` and operands.
+//!
+//! For each SSA one, we compute a symbolic representation of values that are assigned to SSA
+//! locals. This symbolic representation is defined by the `Value` enum. Each produced instance of
+//! `Value` is interned as a `VnIndex`, which allows us to cheaply compute identical values.
+//!
+//! For each non-SSA
 //! one, we compute the `VnIndex` of the rvalue. If this `VnIndex` is associated to a constant, we
 //! replace the rvalue/operand by that constant. Otherwise, if there is an SSA local `y`
 //! associated to this `VnIndex`, and if its definition location strictly dominates the assignment
@@ -91,7 +93,7 @@ use rustc_const_eval::interpret::{
     ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar,
     intern_const_alloc_for_constprop,
 };
-use rustc_data_structures::fx::FxIndexSet;
+use rustc_data_structures::fx::{FxIndexSet, MutableValues};
 use rustc_data_structures::graph::dominators::Dominators;
 use rustc_hir::def::DefKind;
 use rustc_index::bit_set::DenseBitSet;
@@ -107,7 +109,7 @@ use rustc_span::def_id::DefId;
 use smallvec::SmallVec;
 use tracing::{debug, instrument, trace};
 
-use crate::ssa::{AssignedValue, SsaLocals};
+use crate::ssa::SsaLocals;
 
 pub(super) struct GVN;
 
@@ -126,31 +128,11 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
         let dominators = body.basic_blocks.dominators().clone();
 
         let mut state = VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls);
-        ssa.for_each_assignment_mut(
-            body.basic_blocks.as_mut_preserves_cfg(),
-            |local, value, location| {
-                let value = match value {
-                    // We do not know anything of this assigned value.
-                    AssignedValue::Arg | AssignedValue::Terminator => None,
-                    // Try to get some insight.
-                    AssignedValue::Rvalue(rvalue) => {
-                        let value = state.simplify_rvalue(rvalue, location);
-                        // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
-                        // `local` as reusable if we have an exact type match.
-                        if state.local_decls[local].ty != rvalue.ty(state.local_decls, tcx) {
-                            return;
-                        }
-                        value
-                    }
-                };
-                // `next_opaque` is `Some`, so `new_opaque` must return `Some`.
-                let value = value.or_else(|| state.new_opaque()).unwrap();
-                state.assign(local, value);
-            },
-        );
 
-        // Stop creating opaques during replacement as it is useless.
-        state.next_opaque = None;
+        for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) {
+            let opaque = state.new_opaque();
+            state.assign(local, opaque);
+        }
 
         let reverse_postorder = body.basic_blocks.reverse_postorder().to_vec();
         for bb in reverse_postorder {
@@ -250,14 +232,14 @@ struct VnState<'body, 'tcx> {
     locals: IndexVec<Local, Option<VnIndex>>,
     /// Locals that are assigned that value.
     // This vector does not hold all the values of `VnIndex` that we create.
-    // It stops at the largest value created in the first phase of collecting assignments.
     rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>,
     values: FxIndexSet<Value<'tcx>>,
     /// Values evaluated as constants if possible.
     evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>,
     /// Counter to generate different values.
-    /// This is an option to stop creating opaques during replacement.
-    next_opaque: Option<usize>,
+    next_opaque: usize,
+    /// Cache the deref values.
+    derefs: Vec<VnIndex>,
     /// Cache the value of the `unsized_locals` features, to avoid fetching it repeatedly in a loop.
     feature_unsized_locals: bool,
     ssa: &'body SsaLocals,
@@ -289,7 +271,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             rev_locals: IndexVec::with_capacity(num_values),
             values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()),
             evaluated: IndexVec::with_capacity(num_values),
-            next_opaque: Some(1),
+            next_opaque: 1,
+            derefs: Vec::new(),
             feature_unsized_locals: tcx.features().unsized_locals(),
             ssa,
             dominators,
@@ -310,32 +293,31 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             let evaluated = self.eval_to_const(index);
             let _index = self.evaluated.push(evaluated);
             debug_assert_eq!(index, _index);
-            // No need to push to `rev_locals` if we finished listing assignments.
-            if self.next_opaque.is_some() {
-                let _index = self.rev_locals.push(SmallVec::new());
-                debug_assert_eq!(index, _index);
-            }
+            let _index = self.rev_locals.push(SmallVec::new());
+            debug_assert_eq!(index, _index);
         }
         index
     }
 
+    fn next_opaque(&mut self) -> usize {
+        let next_opaque = self.next_opaque;
+        self.next_opaque += 1;
+        next_opaque
+    }
+
     /// Create a new `Value` for which we have no information at all, except that it is distinct
     /// from all the others.
     #[instrument(level = "trace", skip(self), ret)]
-    fn new_opaque(&mut self) -> Option<VnIndex> {
-        let next_opaque = self.next_opaque.as_mut()?;
-        let value = Value::Opaque(*next_opaque);
-        *next_opaque += 1;
-        Some(self.insert(value))
+    fn new_opaque(&mut self) -> VnIndex {
+        let value = Value::Opaque(self.next_opaque());
+        self.insert(value)
     }
 
     /// Create a new `Value::Address` distinct from all the others.
     #[instrument(level = "trace", skip(self), ret)]
-    fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> Option<VnIndex> {
-        let next_opaque = self.next_opaque.as_mut()?;
-        let value = Value::Address { place, kind, provenance: *next_opaque };
-        *next_opaque += 1;
-        Some(self.insert(value))
+    fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> VnIndex {
+        let value = Value::Address { place, kind, provenance: self.next_opaque() };
+        self.insert(value)
     }
 
     fn get(&self, index: VnIndex) -> &Value<'tcx> {
@@ -345,6 +327,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
     /// Record that `local` is assigned `value`. `local` must be SSA.
     #[instrument(level = "trace", skip(self))]
     fn assign(&mut self, local: Local, value: VnIndex) {
+        debug_assert!(self.ssa.is_ssa(local));
         self.locals[local] = Some(value);
 
         // Only register the value if its type is `Sized`, as we will emit copies of it.
@@ -355,21 +338,19 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         }
     }
 
-    fn insert_constant(&mut self, value: Const<'tcx>) -> Option<VnIndex> {
+    fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex {
         let disambiguator = if value.is_deterministic() {
             // The constant is deterministic, no need to disambiguate.
             0
         } else {
             // Multiple mentions of this constant will yield different values,
             // so assign a different `disambiguator` to ensure they do not get the same `VnIndex`.
-            let next_opaque = self.next_opaque.as_mut()?;
-            let disambiguator = *next_opaque;
-            *next_opaque += 1;
+            let disambiguator = self.next_opaque();
             // `disambiguator: 0` means deterministic.
             debug_assert_ne!(disambiguator, 0);
             disambiguator
         };
-        Some(self.insert(Value::Constant { value, disambiguator }))
+        self.insert(Value::Constant { value, disambiguator })
     }
 
     fn insert_bool(&mut self, flag: bool) -> VnIndex {
@@ -390,6 +371,19 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         self.insert(Value::Aggregate(AggregateTy::Tuple, VariantIdx::ZERO, values))
     }
 
+    fn insert_deref(&mut self, value: VnIndex) -> VnIndex {
+        let value = self.insert(Value::Projection(value, ProjectionElem::Deref));
+        self.derefs.push(value);
+        value
+    }
+
+    fn invalidate_derefs(&mut self) {
+        for deref in std::mem::take(&mut self.derefs) {
+            let opaque = self.next_opaque();
+            *self.values.get_index_mut2(deref.index()).unwrap() = Value::Opaque(opaque);
+        }
+    }
+
     #[instrument(level = "trace", skip(self), ret)]
     fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
         use Value::*;
@@ -648,15 +642,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         let proj = match proj {
             ProjectionElem::Deref => {
                 let ty = place.ty(self.local_decls, self.tcx).ty;
-                // unsound: https://github.com/rust-lang/rust/issues/130853
-                if self.tcx.sess.opts.unstable_opts.unsound_mir_opts
-                    && let Some(Mutability::Not) = ty.ref_mutability()
+                if let Some(Mutability::Not) = ty.ref_mutability()
                     && let Some(pointee_ty) = ty.builtin_deref(true)
                     && pointee_ty.is_freeze(self.tcx, self.typing_env())
                 {
                     // An immutable borrow `_x` always points to the same value for the
                     // lifetime of the borrow, so we can merge all instances of `*_x`.
-                    ProjectionElem::Deref
+                    return Some(self.insert_deref(value));
                 } else {
                     return None;
                 }
@@ -830,7 +822,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         location: Location,
     ) -> Option<VnIndex> {
         match *operand {
-            Operand::Constant(ref constant) => self.insert_constant(constant.const_),
+            Operand::Constant(ref constant) => Some(self.insert_constant(constant.const_)),
             Operand::Copy(ref mut place) | Operand::Move(ref mut place) => {
                 let value = self.simplify_place_value(place, location)?;
                 if let Some(const_) = self.try_as_constant(value) {
@@ -866,11 +858,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             Rvalue::Aggregate(..) => return self.simplify_aggregate(rvalue, location),
             Rvalue::Ref(_, borrow_kind, ref mut place) => {
                 self.simplify_place_projection(place, location);
-                return self.new_pointer(*place, AddressKind::Ref(borrow_kind));
+                return Some(self.new_pointer(*place, AddressKind::Ref(borrow_kind)));
             }
             Rvalue::RawPtr(mutbl, ref mut place) => {
                 self.simplify_place_projection(place, location);
-                return self.new_pointer(*place, AddressKind::Address(mutbl));
+                return Some(self.new_pointer(*place, AddressKind::Address(mutbl)));
             }
             Rvalue::WrapUnsafeBinder(ref mut op, ty) => {
                 let value = self.simplify_operand(op, location)?;
@@ -1034,7 +1026,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
 
             if is_zst {
                 let ty = rvalue.ty(self.local_decls, tcx);
-                return self.insert_constant(Const::zero_sized(ty));
+                return Some(self.insert_constant(Const::zero_sized(ty)));
             }
         }
 
@@ -1063,11 +1055,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             }
         };
 
-        let fields: Option<Vec<_>> = field_ops
+        let mut fields: Vec<_> = field_ops
             .iter_mut()
-            .map(|op| self.simplify_operand(op, location).or_else(|| self.new_opaque()))
+            .map(|op| self.simplify_operand(op, location).unwrap_or_else(|| self.new_opaque()))
             .collect();
-        let mut fields = fields?;
 
         if let AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty } = &mut ty {
             let mut was_updated = false;
@@ -1107,9 +1098,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             }
         }
 
-        // unsound: https://github.com/rust-lang/rust/issues/132353
-        if tcx.sess.opts.unstable_opts.unsound_mir_opts
-            && let AggregateTy::Def(_, _) = ty
+        if let AggregateTy::Def(_, _) = ty
             && let Some(value) =
                 self.simplify_aggregate_to_copy(rvalue, location, &fields, variant_index)
         {
@@ -1195,7 +1184,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             ) if let ty::Slice(..) = to.builtin_deref(true).unwrap().kind()
                 && let ty::Array(_, len) = from.builtin_deref(true).unwrap().kind() =>
             {
-                return self.insert_constant(Const::Ty(self.tcx.types.usize, *len));
+                return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len)));
             }
             _ => Value::UnaryOp(op, arg_index),
         };
@@ -1391,7 +1380,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         if let CastKind::PointerCoercion(ReifyFnPointer | ClosureFnPointer(_), _) = kind {
             // Each reification of a generic fn may get a different pointer.
             // Do not try to merge them.
-            return self.new_opaque();
+            return Some(self.new_opaque());
         }
 
         let mut was_ever_updated = false;
@@ -1507,7 +1496,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         // Trivial case: we are fetching a statically known length.
         let place_ty = place.ty(self.local_decls, self.tcx).ty;
         if let ty::Array(_, len) = place_ty.kind() {
-            return self.insert_constant(Const::Ty(self.tcx.types.usize, *len));
+            return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len)));
         }
 
         let mut inner = self.simplify_place_value(place, location)?;
@@ -1529,7 +1518,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             && let Some(to) = to.builtin_deref(true)
             && let ty::Slice(..) = to.kind()
         {
-            return self.insert_constant(Const::Ty(self.tcx.types.usize, *len));
+            return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len)));
         }
 
         // Fallback: a symbolic `Len`.
@@ -1739,41 +1728,70 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> {
         self.tcx
     }
 
-    fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, location: Location) {
+    fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
         self.simplify_place_projection(place, location);
+        if context.is_mutating_use() && !place.projection.is_empty() {
+            // Non-local mutation maybe invalidate deref.
+            self.invalidate_derefs();
+        }
+        self.super_place(place, context, location);
     }
 
     fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) {
         self.simplify_operand(operand, location);
+        self.super_operand(operand, location);
     }
 
     fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, location: Location) {
         if let StatementKind::Assign(box (ref mut lhs, ref mut rvalue)) = stmt.kind {
             self.simplify_place_projection(lhs, location);
 
-            // Do not try to simplify a constant, it's already in canonical shape.
-            if matches!(rvalue, Rvalue::Use(Operand::Constant(_))) {
-                return;
+            let value = self.simplify_rvalue(rvalue, location);
+            let value = if let Some(local) = lhs.as_local()
+                && self.ssa.is_ssa(local)
+                // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
+                // `local` as reusable if we have an exact type match.
+                && self.local_decls[local].ty == rvalue.ty(self.local_decls, self.tcx)
+            {
+                let value = value.unwrap_or_else(|| self.new_opaque());
+                self.assign(local, value);
+                Some(value)
+            } else {
+                value
+            };
+            if let Some(value) = value {
+                if let Some(const_) = self.try_as_constant(value) {
+                    *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_)));
+                } else if let Some(local) = self.try_as_local(value, location)
+                    && *rvalue != Rvalue::Use(Operand::Move(local.into()))
+                {
+                    *rvalue = Rvalue::Use(Operand::Copy(local.into()));
+                    self.reused_locals.insert(local);
+                }
             }
+        }
+        self.super_statement(stmt, location);
+    }
 
-            let value = lhs
-                .as_local()
-                .and_then(|local| self.locals[local])
-                .or_else(|| self.simplify_rvalue(rvalue, location));
-            let Some(value) = value else { return };
-
-            if let Some(const_) = self.try_as_constant(value) {
-                *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_)));
-            } else if let Some(local) = self.try_as_local(value, location)
-                && *rvalue != Rvalue::Use(Operand::Move(local.into()))
+    fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
+        if let Terminator { kind: TerminatorKind::Call { destination, .. }, .. } = terminator {
+            if let Some(local) = destination.as_local()
+                && self.ssa.is_ssa(local)
             {
-                *rvalue = Rvalue::Use(Operand::Copy(local.into()));
-                self.reused_locals.insert(local);
+                let opaque = self.new_opaque();
+                self.assign(local, opaque);
             }
-
-            return;
         }
-        self.super_statement(stmt, location);
+        // Function calls and ASM may invalidate (nested) derefs. We must handle them carefully.
+        // Currently, only preserving derefs for trivial terminators like SwitchInt and Goto.
+        let safe_to_preserve_derefs = matches!(
+            terminator.kind,
+            TerminatorKind::SwitchInt { .. } | TerminatorKind::Goto { .. }
+        );
+        if !safe_to_preserve_derefs {
+            self.invalidate_derefs();
+        }
+        self.super_terminator(terminator, location);
     }
 }
 
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 205d388f4fb..6429d3f67ec 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -12,6 +12,7 @@
 #![feature(map_try_insert)]
 #![feature(never_type)]
 #![feature(try_blocks)]
+#![feature(vec_deque_pop_if)]
 #![feature(yeet_expr)]
 // tidy-alphabetical-end
 
@@ -528,7 +529,7 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> &
         | DefKind::Static { .. }
         | DefKind::Const
         | DefKind::AssocConst => {
-            if let Err(guar) = tcx.check_well_formed(root.expect_local()) {
+            if let Err(guar) = tcx.ensure_ok().check_well_formed(root.expect_local()) {
                 body.tainted_by_errors = Some(guar);
             }
         }
diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs
index 3d512fb064e..edd0cabca49 100644
--- a/compiler/rustc_mir_transform/src/ssa.rs
+++ b/compiler/rustc_mir_transform/src/ssa.rs
@@ -32,12 +32,6 @@ pub(super) struct SsaLocals {
     borrowed_locals: DenseBitSet<Local>,
 }
 
-pub(super) enum AssignedValue<'a, 'tcx> {
-    Arg,
-    Rvalue(&'a mut Rvalue<'tcx>),
-    Terminator,
-}
-
 impl SsaLocals {
     pub(super) fn new<'tcx>(
         tcx: TyCtxt<'tcx>,
@@ -152,38 +146,6 @@ impl SsaLocals {
         })
     }
 
-    pub(super) fn for_each_assignment_mut<'tcx>(
-        &self,
-        basic_blocks: &mut IndexSlice<BasicBlock, BasicBlockData<'tcx>>,
-        mut f: impl FnMut(Local, AssignedValue<'_, 'tcx>, Location),
-    ) {
-        for &local in &self.assignment_order {
-            match self.assignments[local] {
-                Set1::One(DefLocation::Argument) => f(
-                    local,
-                    AssignedValue::Arg,
-                    Location { block: START_BLOCK, statement_index: 0 },
-                ),
-                Set1::One(DefLocation::Assignment(loc)) => {
-                    let bb = &mut basic_blocks[loc.block];
-                    // `loc` must point to a direct assignment to `local`.
-                    let stmt = &mut bb.statements[loc.statement_index];
-                    let StatementKind::Assign(box (target, ref mut rvalue)) = stmt.kind else {
-                        bug!()
-                    };
-                    assert_eq!(target.as_local(), Some(local));
-                    f(local, AssignedValue::Rvalue(rvalue), loc)
-                }
-                Set1::One(DefLocation::CallReturn { call, .. }) => {
-                    let bb = &mut basic_blocks[call];
-                    let loc = Location { block: call, statement_index: bb.statements.len() };
-                    f(local, AssignedValue::Terminator, loc)
-                }
-                _ => {}
-            }
-        }
-    }
-
     /// Compute the equivalence classes for locals, based on copy statements.
     ///
     /// The returned vector maps each local to the one it copies. In the following case:
diff --git a/compiler/rustc_monomorphize/messages.ftl b/compiler/rustc_monomorphize/messages.ftl
index aae2d79c161..6b6653e7de0 100644
--- a/compiler/rustc_monomorphize/messages.ftl
+++ b/compiler/rustc_monomorphize/messages.ftl
@@ -48,7 +48,7 @@ monomorphize_large_assignments =
     .note = The current maximum size is {$limit}, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
 
 monomorphize_no_optimized_mir =
-    missing optimized MIR for an item in the crate `{$crate_name}`
+    missing optimized MIR for `{$instance}` in the crate `{$crate_name}`
     .note = missing optimized MIR for this item (was the crate `{$crate_name}` compiled with `--emit=metadata`?)
 
 monomorphize_recursion_limit =
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 2a1b20ba48b..6e676ac6b8d 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -989,6 +989,7 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) ->
         tcx.dcx().emit_fatal(NoOptimizedMir {
             span: tcx.def_span(def_id),
             crate_name: tcx.crate_name(def_id.krate),
+            instance: instance.to_string(),
         });
     }
 
diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs
index dffa372279f..adfe096f0cd 100644
--- a/compiler/rustc_monomorphize/src/errors.rs
+++ b/compiler/rustc_monomorphize/src/errors.rs
@@ -24,6 +24,7 @@ pub(crate) struct NoOptimizedMir {
     #[note]
     pub span: Span,
     pub crate_name: Symbol,
+    pub instance: String,
 }
 
 #[derive(LintDiagnostic)]
diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs
index 259b39e2b9e..c4b6b18c45d 100644
--- a/compiler/rustc_next_trait_solver/src/delegate.rs
+++ b/compiler/rustc_next_trait_solver/src/delegate.rs
@@ -62,14 +62,12 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
         universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
     ) -> <Self::Interner as Interner>::GenericArg;
 
-    // FIXME: Can we implement this in terms of `add` and `inject`?
-    fn insert_hidden_type(
+    fn register_hidden_type_in_storage(
         &self,
         opaque_type_key: ty::OpaqueTypeKey<Self::Interner>,
-        param_env: <Self::Interner as Interner>::ParamEnv,
         hidden_ty: <Self::Interner as Interner>::Ty,
-        goals: &mut Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
-    ) -> Result<(), NoSolution>;
+        span: <Self::Interner as Interner>::Span,
+    ) -> Option<<Self::Interner as Interner>::Ty>;
 
     fn add_item_bounds_for_hidden_type(
         &self,
@@ -79,14 +77,6 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
         hidden_ty: <Self::Interner as Interner>::Ty,
         goals: &mut Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
     );
-
-    fn inject_new_hidden_type_unchecked(
-        &self,
-        key: ty::OpaqueTypeKey<Self::Interner>,
-        hidden_ty: <Self::Interner as Interner>::Ty,
-        span: <Self::Interner as Interner>::Span,
-    );
-
     fn reset_opaque_types(&self);
 
     fn fetch_eligible_assoc_item(
diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
index a5142de2d39..c2fb592c3f3 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
@@ -37,12 +37,16 @@ where
         | ty::Never
         | ty::Char => Ok(ty::Binder::dummy(vec![])),
 
+        // This branch is only for `experimental_default_bounds`.
+        // Other foreign types were rejected earlier in
+        // `disqualify_auto_trait_candidate_due_to_possible_impl`.
+        ty::Foreign(..) => Ok(ty::Binder::dummy(vec![])),
+
         // Treat `str` like it's defined as `struct str([u8]);`
         ty::Str => Ok(ty::Binder::dummy(vec![Ty::new_slice(cx, Ty::new_u8(cx))])),
 
         ty::Dynamic(..)
         | ty::Param(..)
-        | ty::Foreign(..)
         | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
         | ty::Placeholder(..)
         | ty::Bound(..)
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
index ac6b521f665..4edc293ad80 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
@@ -425,7 +425,8 @@ where
 
     fn register_new_opaque_types(&mut self, opaque_types: &[(ty::OpaqueTypeKey<I>, I::Ty)]) {
         for &(key, ty) in opaque_types {
-            self.delegate.inject_new_hidden_type_unchecked(key, ty, self.origin_span);
+            let prev = self.delegate.register_hidden_type_in_storage(key, ty, self.origin_span);
+            assert_eq!(prev, None);
         }
     }
 }
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
index 7ef36d0e9ae..148ba02252d 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
@@ -387,7 +387,8 @@ where
         };
 
         for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
-            ecx.delegate.inject_new_hidden_type_unchecked(key, ty, ecx.origin_span);
+            let prev = ecx.delegate.register_hidden_type_in_storage(key, ty, ecx.origin_span);
+            assert_eq!(prev, None);
         }
 
         if !ecx.nested_goals.is_empty() {
@@ -1070,16 +1071,12 @@ where
         self.delegate.fetch_eligible_assoc_item(goal_trait_ref, trait_assoc_def_id, impl_def_id)
     }
 
-    pub(super) fn insert_hidden_type(
+    pub(super) fn register_hidden_type_in_storage(
         &mut self,
         opaque_type_key: ty::OpaqueTypeKey<I>,
-        param_env: I::ParamEnv,
         hidden_ty: I::Ty,
-    ) -> Result<(), NoSolution> {
-        let mut goals = Vec::new();
-        self.delegate.insert_hidden_type(opaque_type_key, param_env, hidden_ty, &mut goals)?;
-        self.add_goals(GoalSource::Misc, goals);
-        Ok(())
+    ) -> Option<I::Ty> {
+        self.delegate.register_hidden_type_in_storage(opaque_type_key, hidden_ty, self.origin_span)
     }
 
     pub(super) fn add_item_bounds_for_hidden_type(
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
index 817dffa127b..82dae51b3d0 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
@@ -86,8 +86,8 @@ where
                 }
 
                 // Otherwise, define a new opaque type
-                // FIXME: should we use `inject_hidden_type_unchecked` here?
-                self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
+                let prev = self.register_hidden_type_in_storage(opaque_type_key, expected);
+                assert_eq!(prev, None);
                 self.add_item_bounds_for_hidden_type(
                     def_id.into(),
                     opaque_ty.args,
diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
index b72f776e5cb..b7ae96ec9d2 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -1086,6 +1086,25 @@ where
         goal: Goal<I, TraitPredicate<I>>,
     ) -> Option<Result<Candidate<I>, NoSolution>> {
         let self_ty = goal.predicate.self_ty();
+        let check_impls = || {
+            let mut disqualifying_impl = None;
+            self.cx().for_each_relevant_impl(
+                goal.predicate.def_id(),
+                goal.predicate.self_ty(),
+                |impl_def_id| {
+                    disqualifying_impl = Some(impl_def_id);
+                },
+            );
+            if let Some(def_id) = disqualifying_impl {
+                trace!(?def_id, ?goal, "disqualified auto-trait implementation");
+                // No need to actually consider the candidate here,
+                // since we do that in `consider_impl_candidate`.
+                return Some(Err(NoSolution));
+            } else {
+                None
+            }
+        };
+
         match self_ty.kind() {
             // Stall int and float vars until they are resolved to a concrete
             // numerical type. That's because the check for impls below treats
@@ -1096,6 +1115,10 @@ where
                 Some(self.forced_ambiguity(MaybeCause::Ambiguity))
             }
 
+            // Backward compatibility for default auto traits.
+            // Test: ui/traits/default_auto_traits/extern-types.rs
+            ty::Foreign(..) if self.cx().is_default_trait(goal.predicate.def_id()) => check_impls(),
+
             // These types cannot be structurally decomposed into constituent
             // types, and therefore have no built-in auto impl.
             ty::Dynamic(..)
@@ -1156,24 +1179,7 @@ where
             | ty::Never
             | ty::Tuple(_)
             | ty::Adt(_, _)
-            | ty::UnsafeBinder(_) => {
-                let mut disqualifying_impl = None;
-                self.cx().for_each_relevant_impl(
-                    goal.predicate.def_id(),
-                    goal.predicate.self_ty(),
-                    |impl_def_id| {
-                        disqualifying_impl = Some(impl_def_id);
-                    },
-                );
-                if let Some(def_id) = disqualifying_impl {
-                    trace!(?def_id, ?goal, "disqualified auto-trait implementation");
-                    // No need to actually consider the candidate here,
-                    // since we do that in `consider_impl_candidate`.
-                    return Some(Err(NoSolution));
-                } else {
-                    None
-                }
-            }
+            | ty::UnsafeBinder(_) => check_impls(),
             ty::Error(_) => None,
         }
     }
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index e1e6b93abf3..841d967d934 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -14,10 +14,10 @@ use rustc_ast::util::classify;
 use rustc_ast::util::parser::{AssocOp, ExprPrecedence, Fixity, prec_let_scrutinee_needs_par};
 use rustc_ast::visit::{Visitor, walk_expr};
 use rustc_ast::{
-    self as ast, AnonConst, Arm, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy,
-    ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl, FnRetTy, Label, MacCall,
-    MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind,
-    YieldKind,
+    self as ast, AnonConst, Arm, AssignOp, AssignOpKind, AttrStyle, AttrVec, BinOp, BinOpKind,
+    BlockCheckMode, CaptureBy, ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl,
+    FnRetTy, Label, MacCall, MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind,
+    UnOp, UnsafeBinderCastKind, YieldKind,
 };
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic};
@@ -359,7 +359,7 @@ impl<'a> Parser<'a> {
             (
                 Some(
                     AssocOp::Binary(BinOpKind::Shr | BinOpKind::Gt | BinOpKind::Ge)
-                    | AssocOp::AssignOp(BinOpKind::Shr),
+                    | AssocOp::AssignOp(AssignOpKind::ShrAssign),
                 ),
                 _,
             ) if self.restrictions.contains(Restrictions::CONST_EXPR) => {
@@ -3914,8 +3914,8 @@ impl<'a> Parser<'a> {
         self.dcx().emit_err(errors::LeftArrowOperator { span });
     }
 
-    fn mk_assign_op(&self, binop: BinOp, lhs: P<Expr>, rhs: P<Expr>) -> ExprKind {
-        ExprKind::AssignOp(binop, lhs, rhs)
+    fn mk_assign_op(&self, assign_op: AssignOp, lhs: P<Expr>, rhs: P<Expr>) -> ExprKind {
+        ExprKind::AssignOp(assign_op, lhs, rhs)
     }
 
     fn mk_range(
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index a8208e4717b..9405b58ab3b 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -2960,13 +2960,30 @@ impl<'a> Parser<'a> {
                 let parser_snapshot_before_ty = this.create_snapshot_for_diagnostic();
                 this.eat_incorrect_doc_comment_for_param_type();
                 let mut ty = this.parse_ty_for_param();
-                if ty.is_ok()
-                    && this.token != token::Comma
-                    && this.token != token::CloseDelim(Delimiter::Parenthesis)
-                {
-                    // This wasn't actually a type, but a pattern looking like a type,
-                    // so we are going to rollback and re-parse for recovery.
-                    ty = this.unexpected_any();
+
+                if let Ok(t) = &ty {
+                    // Check for trailing angle brackets
+                    if let TyKind::Path(_, Path { segments, .. }) = &t.kind {
+                        if let Some(segment) = segments.last() {
+                            if let Some(guar) =
+                                this.check_trailing_angle_brackets(segment, &[exp!(CloseParen)])
+                            {
+                                return Ok((
+                                    dummy_arg(segment.ident, guar),
+                                    Trailing::No,
+                                    UsePreAttrPos::No,
+                                ));
+                            }
+                        }
+                    }
+
+                    if this.token != token::Comma
+                        && this.token != token::CloseDelim(Delimiter::Parenthesis)
+                    {
+                        // This wasn't actually a type, but a pattern looking like a type,
+                        // so we are going to rollback and re-parse for recovery.
+                        ty = this.unexpected_any();
+                    }
                 }
                 match ty {
                     Ok(ty) => {
@@ -2977,6 +2994,7 @@ impl<'a> Parser<'a> {
                     }
                     // If this is a C-variadic argument and we hit an error, return the error.
                     Err(err) if this.token == token::DotDotDot => return Err(err),
+                    Err(err) if this.unmatched_angle_bracket_count > 0 => return Err(err),
                     // Recover from attempting to parse the argument as a type without pattern.
                     Err(err) => {
                         err.cancel();
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 97cd4d2117f..2cd09aa8959 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -73,7 +73,20 @@ impl<'a> Parser<'a> {
             });
         }
 
-        let stmt = if self.token.is_keyword(kw::Let) {
+        let stmt = if self.token.is_keyword(kw::Super) && self.is_keyword_ahead(1, &[kw::Let]) {
+            self.collect_tokens(None, attrs, force_collect, |this, attrs| {
+                this.expect_keyword(exp!(Super))?;
+                this.psess.gated_spans.gate(sym::super_let, this.prev_token.span);
+                this.expect_keyword(exp!(Let))?;
+                let local = this.parse_local(attrs)?; // FIXME(mara): implement super let
+                let trailing = Trailing::from(capture_semi && this.token == token::Semi);
+                Ok((
+                    this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
+                    trailing,
+                    UsePreAttrPos::No,
+                ))
+            })?
+        } else if self.token.is_keyword(kw::Let) {
             self.collect_tokens(None, attrs, force_collect, |this, attrs| {
                 this.expect_keyword(exp!(Let))?;
                 let local = this.parse_local(attrs)?;
diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs
index 886438fd583..add3c970201 100644
--- a/compiler/rustc_parse/src/parser/token_type.rs
+++ b/compiler/rustc_parse/src/parser/token_type.rs
@@ -114,6 +114,7 @@ pub enum TokenType {
     KwSelfUpper,
     KwStatic,
     KwStruct,
+    KwSuper,
     KwTrait,
     KwTry,
     KwType,
@@ -250,6 +251,7 @@ impl TokenType {
             KwSelfUpper,
             KwStatic,
             KwStruct,
+            KwSuper,
             KwTrait,
             KwTry,
             KwType,
@@ -324,6 +326,7 @@ impl TokenType {
             TokenType::KwSelfUpper => Some(kw::SelfUpper),
             TokenType::KwStatic => Some(kw::Static),
             TokenType::KwStruct => Some(kw::Struct),
+            TokenType::KwSuper => Some(kw::Super),
             TokenType::KwTrait => Some(kw::Trait),
             TokenType::KwTry => Some(kw::Try),
             TokenType::KwType => Some(kw::Type),
@@ -549,6 +552,7 @@ macro_rules! exp {
     (SelfUpper)      => { exp!(@kw, SelfUpper,  KwSelfUpper) };
     (Static)         => { exp!(@kw, Static,     KwStatic) };
     (Struct)         => { exp!(@kw, Struct,     KwStruct) };
+    (Super)          => { exp!(@kw, Super,      KwSuper) };
     (Trait)          => { exp!(@kw, Trait,      KwTrait) };
     (Try)            => { exp!(@kw, Try,        KwTry) };
     (Type)           => { exp!(@kw, Type,       KwType) };
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index cfc0369c598..669349f3380 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -1157,7 +1157,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                     errors::DocInlineOnlyUse {
                         attr_span: meta.span(),
                         item_span: (attr.style() == AttrStyle::Outer)
-                            .then(|| self.tcx.hir().span(hir_id)),
+                            .then(|| self.tcx.hir_span(hir_id)),
                     },
                 );
             }
@@ -1179,7 +1179,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 errors::DocMaskedOnlyExternCrate {
                     attr_span: meta.span(),
                     item_span: (attr.style() == AttrStyle::Outer)
-                        .then(|| self.tcx.hir().span(hir_id)),
+                        .then(|| self.tcx.hir_span(hir_id)),
                 },
             );
             return;
@@ -1193,7 +1193,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 errors::DocMaskedNotExternCrateSelf {
                     attr_span: meta.span(),
                     item_span: (attr.style() == AttrStyle::Outer)
-                        .then(|| self.tcx.hir().span(hir_id)),
+                        .then(|| self.tcx.hir_span(hir_id)),
                 },
             );
         }
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index f77e1db42d4..0060e726a8e 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -19,8 +19,8 @@ use rustc_middle::middle::privacy::Level;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_middle::{bug, span_bug};
-use rustc_session::lint;
 use rustc_session::lint::builtin::DEAD_CODE;
+use rustc_session::lint::{self, LintExpectationId};
 use rustc_span::{Symbol, sym};
 
 use crate::errors::{
@@ -421,10 +421,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                 }
                 hir::ItemKind::ForeignMod { .. } => {}
                 hir::ItemKind::Trait(..) => {
-                    for impl_def_id in self.tcx.all_impls(item.owner_id.to_def_id()) {
-                        if let Some(local_def_id) = impl_def_id.as_local()
-                            && let ItemKind::Impl(impl_ref) =
-                                self.tcx.hir_expect_item(local_def_id).kind
+                    for &impl_def_id in self.tcx.local_trait_impls(item.owner_id.def_id) {
+                        if let ItemKind::Impl(impl_ref) = self.tcx.hir_expect_item(impl_def_id).kind
                         {
                             // skip items
                             // mark dependent traits live
@@ -698,8 +696,8 @@ fn has_allow_dead_code_or_lang_attr(
 
     fn has_allow_expect_dead_code(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
         let hir_id = tcx.local_def_id_to_hir_id(def_id);
-        let lint_level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).0;
-        matches!(lint_level, lint::Allow | lint::Expect(_))
+        let lint_level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).level;
+        matches!(lint_level, lint::Allow | lint::Expect)
     }
 
     fn has_used_like_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
@@ -917,7 +915,7 @@ fn live_symbols_and_ignored_derived_traits(
 struct DeadItem {
     def_id: LocalDefId,
     name: Symbol,
-    level: lint::Level,
+    level: (lint::Level, Option<LintExpectationId>),
 }
 
 struct DeadVisitor<'tcx> {
@@ -961,9 +959,10 @@ impl<'tcx> DeadVisitor<'tcx> {
         ShouldWarnAboutField::Yes
     }
 
-    fn def_lint_level(&self, id: LocalDefId) -> lint::Level {
+    fn def_lint_level(&self, id: LocalDefId) -> (lint::Level, Option<LintExpectationId>) {
         let hir_id = self.tcx.local_def_id_to_hir_id(id);
-        self.tcx.lint_level_at_node(DEAD_CODE, hir_id).0
+        let level = self.tcx.lint_level_at_node(DEAD_CODE, hir_id);
+        (level.level, level.lint_id)
     }
 
     // # Panics
@@ -1131,7 +1130,8 @@ impl<'tcx> DeadVisitor<'tcx> {
         if dead_codes.is_empty() {
             return;
         }
-        dead_codes.sort_by_key(|v| v.level);
+        // FIXME: `dead_codes` should probably be morally equivalent to `IndexMap<(Level, LintExpectationId), (DefId, Symbol)>`
+        dead_codes.sort_by_key(|v| v.level.0);
         for group in dead_codes.chunk_by(|a, b| a.level == b.level) {
             self.lint_at_single_level(&group, participle, Some(def_id), report_on);
         }
diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs
index e13d94c1031..17a729f422a 100644
--- a/compiler/rustc_passes/src/diagnostic_items.rs
+++ b/compiler/rustc_passes/src/diagnostic_items.rs
@@ -41,8 +41,8 @@ fn report_duplicate_item(
     original_def_id: DefId,
     item_def_id: DefId,
 ) {
-    let orig_span = tcx.hir().span_if_local(original_def_id);
-    let duplicate_span = tcx.hir().span_if_local(item_def_id);
+    let orig_span = tcx.hir_span_if_local(original_def_id);
+    let duplicate_span = tcx.hir_span_if_local(item_def_id);
     tcx.dcx().emit_err(DuplicateDiagnosticItemInCrate {
         duplicate_span,
         orig_span,
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 9e55914f8f2..d7baad69c78 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -726,7 +726,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index {
 
         annotator.annotate(
             CRATE_DEF_ID,
-            tcx.hir().span(CRATE_HIR_ID),
+            tcx.hir_span(CRATE_HIR_ID),
             None,
             AnnotationKind::Required,
             InheritDeprecation::Yes,
@@ -980,7 +980,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                                     // Calculating message for lint involves calling `self.def_path_str`,
                                     // which will by default invoke the expensive `visible_parent_map` query.
                                     // Skip all that work if the lint is allowed anyway.
-                                    if self.tcx.lint_level_at_node(DEPRECATED, id).0
+                                    if self.tcx.lint_level_at_node(DEPRECATED, id).level
                                         == lint::Level::Allow
                                     {
                                         return;
@@ -1099,7 +1099,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
     if is_staged_api {
         let effective_visibilities = &tcx.effective_visibilities(());
         let mut missing = MissingStabilityAnnotations { tcx, effective_visibilities };
-        missing.check_missing_stability(CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID));
+        missing.check_missing_stability(CRATE_DEF_ID, tcx.hir_span(CRATE_HIR_ID));
         tcx.hir_walk_toplevel_module(&mut missing);
         tcx.hir_visit_all_item_likes_in_crate(&mut missing);
     }
diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs
index 585cda1d24b..3da744dc8c0 100644
--- a/compiler/rustc_pattern_analysis/src/lints.rs
+++ b/compiler/rustc_pattern_analysis/src/lints.rs
@@ -1,3 +1,4 @@
+use rustc_middle::lint::LevelAndSource;
 use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
 use rustc_span::ErrorGuaranteed;
 use tracing::instrument;
@@ -64,7 +65,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>(
     scrut_ty: RevealedTy<'tcx>,
 ) -> Result<(), ErrorGuaranteed> {
     if !matches!(
-        rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level).0,
+        rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level).level,
         rustc_session::lint::Level::Allow
     ) {
         let witnesses = collect_nonexhaustive_missing_variants(rcx, pat_column)?;
@@ -88,13 +89,13 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>(
         // 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) =
+            let LevelAndSource { level, src, .. } =
                 rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data);
-            if !matches!(lint_level, rustc_session::lint::Level::Allow) {
+            if !matches!(level, rustc_session::lint::Level::Allow) {
                 let decorator = NonExhaustiveOmittedPatternLintOnArm {
-                    lint_span: lint_level_source.span(),
+                    lint_span: src.span(),
                     suggest_lint_on_match: rcx.whole_match_span.map(|span| span.shrink_to_lo()),
-                    lint_level: lint_level.as_str(),
+                    lint_level: level.as_str(),
                     lint_name: "non_exhaustive_omitted_patterns",
                 };
 
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index 3238c7a0912..19ccc5587d6 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -870,6 +870,17 @@ macro_rules! define_queries {
                 }
             }
 
+            pub(crate) fn AnonZeroDeps<'tcx>() -> DepKindStruct<'tcx> {
+                DepKindStruct {
+                    is_anon: true,
+                    is_eval_always: false,
+                    fingerprint_style: FingerprintStyle::Opaque,
+                    force_from_dep_node: Some(|_, _, _| bug!("cannot force an anon node")),
+                    try_load_from_on_disk_cache: None,
+                    name: &"AnonZeroDeps",
+                }
+            }
+
             pub(crate) fn TraitSelect<'tcx>() -> DepKindStruct<'tcx> {
                 DepKindStruct {
                     is_anon: true,
diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs
index 127dcd825da..9f34417973e 100644
--- a/compiler/rustc_query_system/src/dep_graph/graph.rs
+++ b/compiler/rustc_query_system/src/dep_graph/graph.rs
@@ -50,7 +50,7 @@ rustc_index::newtype_index! {
 rustc_data_structures::static_assert_size!(Option<DepNodeIndex>, 4);
 
 impl DepNodeIndex {
-    const SINGLETON_DEPENDENCYLESS_ANON_NODE: DepNodeIndex = DepNodeIndex::ZERO;
+    const SINGLETON_ZERO_DEPS_ANON_NODE: DepNodeIndex = DepNodeIndex::ZERO;
     pub const FOREVER_RED_NODE: DepNodeIndex = DepNodeIndex::from_u32(1);
 }
 
@@ -140,13 +140,13 @@ impl<D: Deps> DepGraph<D> {
 
         let colors = DepNodeColorMap::new(prev_graph_node_count);
 
-        // Instantiate a dependy-less node only once for anonymous queries.
+        // Instantiate a node with zero dependencies only once for anonymous queries.
         let _green_node_index = current.alloc_node(
-            DepNode { kind: D::DEP_KIND_NULL, hash: current.anon_id_seed.into() },
+            DepNode { kind: D::DEP_KIND_ANON_ZERO_DEPS, hash: current.anon_id_seed.into() },
             EdgesVec::new(),
             Fingerprint::ZERO,
         );
-        assert_eq!(_green_node_index, DepNodeIndex::SINGLETON_DEPENDENCYLESS_ANON_NODE);
+        assert_eq!(_green_node_index, DepNodeIndex::SINGLETON_ZERO_DEPS_ANON_NODE);
 
         // Instantiate a dependy-less red node only once for anonymous queries.
         let red_node_index = current.alloc_node(
@@ -407,7 +407,7 @@ impl<D: Deps> DepGraphData<D> {
                 // going to be (i.e. equal to the precomputed
                 // `SINGLETON_DEPENDENCYLESS_ANON_NODE`). As a consequence we can skip creating
                 // a `StableHasher` and sending the node through interning.
-                DepNodeIndex::SINGLETON_DEPENDENCYLESS_ANON_NODE
+                DepNodeIndex::SINGLETON_ZERO_DEPS_ANON_NODE
             }
             1 => {
                 // When there is only one dependency, don't bother creating a node.
diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs
index 4eeb6078d14..3a80835afad 100644
--- a/compiler/rustc_query_system/src/dep_graph/mod.rs
+++ b/compiler/rustc_query_system/src/dep_graph/mod.rs
@@ -111,6 +111,9 @@ pub trait Deps {
     /// We use this to create a side effect node.
     const DEP_KIND_SIDE_EFFECT: DepKind;
 
+    /// We use this to create the anon node with zero dependencies.
+    const DEP_KIND_ANON_ZERO_DEPS: DepKind;
+
     /// This is the highest value a `DepKind` can have. It's used during encoding to
     /// pack information into the unused bits.
     const DEP_KIND_MAX: u16;
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 612e091770f..3ac66840d87 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -88,6 +88,8 @@ mod late;
 mod macros;
 pub mod rustdoc;
 
+pub use macros::registered_tools_ast;
+
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
 #[derive(Debug)]
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index d0ffef798cf..9d6ae0aa9d1 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -10,7 +10,7 @@ use rustc_ast::{self as ast, Crate, NodeId, attr};
 use rustc_ast_pretty::pprust;
 use rustc_attr_parsing::{AttributeKind, StabilityLevel, find_attr};
 use rustc_data_structures::intern::Interned;
-use rustc_errors::{Applicability, StashKey};
+use rustc_errors::{Applicability, DiagCtxtHandle, StashKey};
 use rustc_expand::base::{
     DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension, SyntaxExtensionKind,
 };
@@ -124,14 +124,21 @@ fn fast_print_path(path: &ast::Path) -> Symbol {
 }
 
 pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools {
-    let mut registered_tools = RegisteredTools::default();
     let (_, pre_configured_attrs) = &*tcx.crate_for_resolver(()).borrow();
+    registered_tools_ast(tcx.dcx(), pre_configured_attrs)
+}
+
+pub fn registered_tools_ast(
+    dcx: DiagCtxtHandle<'_>,
+    pre_configured_attrs: &[ast::Attribute],
+) -> RegisteredTools {
+    let mut registered_tools = RegisteredTools::default();
     for attr in attr::filter_by_name(pre_configured_attrs, sym::register_tool) {
         for meta_item_inner in attr.meta_item_list().unwrap_or_default() {
             match meta_item_inner.ident() {
                 Some(ident) => {
                     if let Some(old_ident) = registered_tools.replace(ident) {
-                        tcx.dcx().emit_err(errors::ToolWasAlreadyRegistered {
+                        dcx.emit_err(errors::ToolWasAlreadyRegistered {
                             span: ident.span,
                             tool: ident,
                             old_ident_span: old_ident.span,
@@ -139,7 +146,7 @@ pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools {
                     }
                 }
                 None => {
-                    tcx.dcx().emit_err(errors::ToolOnlyAcceptsIdentifiers {
+                    dcx.emit_err(errors::ToolOnlyAcceptsIdentifiers {
                         span: meta_item_inner.span(),
                         tool: sym::register_tool,
                     });
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index ed336cc5596..1f18950feac 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -50,6 +50,7 @@ pub const PRINT_KINDS: &[(&str, PrintKind)] = &[
     ("check-cfg", PrintKind::CheckCfg),
     ("code-models", PrintKind::CodeModels),
     ("crate-name", PrintKind::CrateName),
+    ("crate-root-lint-levels", PrintKind::CrateRootLintLevels),
     ("deployment-target", PrintKind::DeploymentTarget),
     ("file-names", PrintKind::FileNames),
     ("host-tuple", PrintKind::HostTuple),
@@ -881,6 +882,7 @@ pub enum PrintKind {
     CheckCfg,
     CodeModels,
     CrateName,
+    CrateRootLintLevels,
     DeploymentTarget,
     FileNames,
     HostTuple,
@@ -1698,7 +1700,7 @@ pub fn get_cmd_lint_options(
     let mut lint_opts_with_position = vec![];
     let mut describe_lints = false;
 
-    for level in [lint::Allow, lint::Warn, lint::ForceWarn(None), lint::Deny, lint::Forbid] {
+    for level in [lint::Allow, lint::Warn, lint::ForceWarn, lint::Deny, lint::Forbid] {
         for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) {
             if lint_name == "help" {
                 describe_lints = true;
@@ -2067,6 +2069,7 @@ fn check_print_request_stability(
     match print_kind {
         PrintKind::AllTargetSpecsJson
         | PrintKind::CheckCfg
+        | PrintKind::CrateRootLintLevels
         | PrintKind::SupportedCrateTypes
         | PrintKind::TargetSpecJson
             if !unstable_opts.unstable_options =>
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index cd5e2c4173e..eb513e00936 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -2187,6 +2187,8 @@ options! {
         "Use WebAssembly error handling for wasm32-unknown-emscripten"),
     enforce_type_length_limit: bool = (false, parse_bool, [TRACKED],
         "enforce the type length limit when monomorphizing instances in codegen"),
+    experimental_default_bounds: bool = (false, parse_bool, [TRACKED],
+        "enable default bounds for experimental group of auto traits"),
     export_executable_symbols: bool = (false, parse_bool, [TRACKED],
         "export symbols from executables, as if they were dynamic libraries"),
     external_clangrt: bool = (false, parse_bool, [UNTRACKED],
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index c6e570e524f..31847ae3b46 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -800,6 +800,15 @@ symbols! {
         default_fn,
         default_lib_allocator,
         default_method_body_is_const,
+        // --------------------------
+        // Lang items which are used only for experiments with auto traits with default bounds.
+        // These lang items are not actually defined in core/std. Experiment is a part of
+        // `MCP: Low level components for async drop`(https://github.com/rust-lang/compiler-team/issues/727)
+        default_trait1,
+        default_trait2,
+        default_trait3,
+        default_trait4,
+        // --------------------------
         default_type_parameter_fallback,
         default_type_params,
         define_opaque,
@@ -1378,6 +1387,7 @@ symbols! {
         movbe_target_feature,
         move_ref_pattern,
         move_size_limit,
+        movrs_target_feature,
         mul,
         mul_assign,
         mul_with_overflow,
@@ -2039,6 +2049,7 @@ symbols! {
         sub_assign,
         sub_with_overflow,
         suggestion,
+        super_let,
         supertrait_item_shadowing,
         surface_async_drop_in_place,
         sym,
diff --git a/compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs
index 8a4bc58e546..efc17d8d083 100644
--- a/compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs
@@ -16,7 +16,7 @@ pub(crate) fn target() -> Target {
             cpu: "generic-rv32".into(),
             llvm_abiname: "ilp32d".into(),
             max_atomic_width: Some(32),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             stack_probes: StackProbeType::Inline,
             ..base::vxworks::opts()
         },
diff --git a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs
index 6dda346aaaf..5b7feef70d0 100644
--- a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs
@@ -17,7 +17,7 @@ pub(crate) fn target() -> Target {
         options: TargetOptions {
             code_model: Some(CodeModel::Medium),
             cpu: "generic-rv32".into(),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             llvm_abiname: "ilp32d".into(),
             max_atomic_width: Some(32),
             supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
diff --git a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs
index ba10e3c6881..938b39b10c6 100644
--- a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs
@@ -19,7 +19,7 @@ pub(crate) fn target() -> Target {
         options: TargetOptions {
             code_model: Some(CodeModel::Medium),
             cpu: "generic-rv32".into(),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             llvm_abiname: "ilp32d".into(),
             max_atomic_width: Some(32),
             supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
diff --git a/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs b/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs
index c8ef737b9e7..9f02ed4bcbe 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs
@@ -19,7 +19,7 @@ pub(crate) fn target() -> Target {
         options: TargetOptions {
             code_model: Some(CodeModel::Medium),
             cpu: "generic-rv64".into(),
-            features: "+m,+a,+f,+d,+c,+zba,+zbb,+zbs,+v".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei,+zba,+zbb,+zbs,+v".into(),
             llvm_abiname: "lp64d".into(),
             supported_sanitizers: SanitizerSet::ADDRESS,
             max_atomic_width: Some(64),
diff --git a/compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs
index 39aa70035e4..8d8c21952de 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs
@@ -16,7 +16,7 @@ pub(crate) fn target() -> Target {
             cpu: "generic-rv64".into(),
             llvm_abiname: "lp64d".into(),
             max_atomic_width: Some(64),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             stack_probes: StackProbeType::Inline,
             ..base::vxworks::opts()
         },
diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs
index ecf65677531..e628095b88a 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs
@@ -15,7 +15,7 @@ pub(crate) fn target() -> Target {
         options: TargetOptions {
             code_model: Some(CodeModel::Medium),
             cpu: "generic-rv64".into(),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             llvm_abiname: "lp64d".into(),
             max_atomic_width: Some(64),
             ..base::freebsd::opts()
diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs
index e260237ca77..c4466e13d14 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs
@@ -4,7 +4,7 @@ pub(crate) fn target() -> Target {
     let mut base = base::fuchsia::opts();
     base.code_model = Some(CodeModel::Medium);
     base.cpu = "generic-rv64".into();
-    base.features = "+m,+a,+f,+d,+c".into();
+    base.features = "+m,+a,+f,+d,+c,+zicsr,+zifencei".into();
     base.llvm_abiname = "lp64d".into();
     base.max_atomic_width = Some(64);
     base.stack_probes = StackProbeType::Inline;
diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_hermit.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_hermit.rs
index 88b5dca284a..5c15bdd9f64 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_hermit.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_hermit.rs
@@ -14,7 +14,7 @@ pub(crate) fn target() -> Target {
         data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(),
         options: TargetOptions {
             cpu: "generic-rv64".into(),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             relocation_model: RelocModel::Pic,
             code_model: Some(CodeModel::Medium),
             tls_model: TlsModel::LocalExec,
diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs
index 8ffb622511d..af2f42fa00a 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs
@@ -17,7 +17,7 @@ pub(crate) fn target() -> Target {
         options: TargetOptions {
             code_model: Some(CodeModel::Medium),
             cpu: "generic-rv64".into(),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             llvm_abiname: "lp64d".into(),
             max_atomic_width: Some(64),
             supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs
index 33b08fdcb05..70c19952af0 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs
@@ -17,7 +17,7 @@ pub(crate) fn target() -> Target {
         options: TargetOptions {
             code_model: Some(CodeModel::Medium),
             cpu: "generic-rv64".into(),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             llvm_abiname: "lp64d".into(),
             max_atomic_width: Some(64),
             supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_netbsd.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_netbsd.rs
index 2b647e36f18..1f359d1e7fe 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_netbsd.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_netbsd.rs
@@ -15,7 +15,7 @@ pub(crate) fn target() -> Target {
         options: TargetOptions {
             code_model: Some(CodeModel::Medium),
             cpu: "generic-rv64".into(),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             llvm_abiname: "lp64d".into(),
             max_atomic_width: Some(64),
             mcount: "__mcount".into(),
diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_none_elf.rs
index d6f0a5499b9..5a5aad93efb 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_none_elf.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_none_elf.rs
@@ -22,7 +22,7 @@ pub(crate) fn target() -> Target {
             llvm_abiname: "lp64d".into(),
             cpu: "generic-rv64".into(),
             max_atomic_width: Some(64),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             code_model: Some(CodeModel::Medium),
diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs
index bc6829897a4..e8abc926dd0 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs
@@ -24,7 +24,7 @@ pub(crate) fn target() -> Target {
             llvm_abiname: "lp64d".into(),
             cpu: "generic-rv64".into(),
             max_atomic_width: Some(64),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             code_model: Some(CodeModel::Medium),
diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_openbsd.rs
index 75f508d8e93..85d7dfe7865 100644
--- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_openbsd.rs
+++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_openbsd.rs
@@ -15,7 +15,7 @@ pub(crate) fn target() -> Target {
         options: TargetOptions {
             code_model: Some(CodeModel::Medium),
             cpu: "generic-rv64".into(),
-            features: "+m,+a,+f,+d,+c".into(),
+            features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(),
             llvm_abiname: "lp64d".into(),
             max_atomic_width: Some(64),
             ..base::openbsd::opts()
diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs
index 0e6523f0880..b4ec1879fed 100644
--- a/compiler/rustc_target/src/target_features.rs
+++ b/compiler/rustc_target/src/target_features.rs
@@ -380,11 +380,16 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     // tidy-alphabetical-start
     ("adx", Stable, &[]),
     ("aes", Stable, &["sse2"]),
+    ("amx-avx512", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
     ("amx-bf16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
     ("amx-complex", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
     ("amx-fp16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
+    ("amx-fp8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
     ("amx-int8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
+    ("amx-movrs", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
+    ("amx-tf32", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
     ("amx-tile", Unstable(sym::x86_amx_intrinsics), &[]),
+    ("amx-transpose", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
     ("avx", Stable, &["sse4.2"]),
     ("avx2", Stable, &["avx"]),
     ("avx512bf16", Unstable(sym::avx512_target_feature), &["avx512bw"]),
@@ -418,6 +423,7 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]),
     ("lzcnt", Stable, &[]),
     ("movbe", Stable, &[]),
+    ("movrs", Unstable(sym::movrs_target_feature), &[]),
     ("pclmulqdq", Stable, &["sse2"]),
     ("popcnt", Stable, &[]),
     ("prfchw", Unstable(sym::prfchw_target_feature), &[]),
@@ -488,7 +494,7 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     ("c", Stable, &[]),
     ("d", Unstable(sym::riscv_target_feature), &["f"]),
     ("e", Unstable(sym::riscv_target_feature), &[]),
-    ("f", Unstable(sym::riscv_target_feature), &[]),
+    ("f", Unstable(sym::riscv_target_feature), &["zicsr"]),
     (
         "forced-atomics",
         Stability::Forbidden { reason: "unsound because it changes the ABI of atomic operations" },
@@ -517,15 +523,20 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     ("zdinx", Unstable(sym::riscv_target_feature), &["zfinx"]),
     ("zfh", Unstable(sym::riscv_target_feature), &["zfhmin"]),
     ("zfhmin", Unstable(sym::riscv_target_feature), &["f"]),
-    ("zfinx", Unstable(sym::riscv_target_feature), &[]),
+    ("zfinx", Unstable(sym::riscv_target_feature), &["zicsr"]),
     ("zhinx", Unstable(sym::riscv_target_feature), &["zhinxmin"]),
     ("zhinxmin", Unstable(sym::riscv_target_feature), &["zfinx"]),
+    ("zicntr", Unstable(sym::riscv_target_feature), &["zicsr"]),
+    ("zicsr", Unstable(sym::riscv_target_feature), &[]),
+    ("zifencei", Unstable(sym::riscv_target_feature), &[]),
+    ("zihintpause", Unstable(sym::riscv_target_feature), &[]),
+    ("zihpm", Unstable(sym::riscv_target_feature), &["zicsr"]),
     ("zk", Stable, &["zkn", "zkr", "zkt"]),
     ("zkn", Stable, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]),
     ("zknd", Stable, &[]),
     ("zkne", Stable, &[]),
     ("zknh", Stable, &[]),
-    ("zkr", Stable, &[]),
+    ("zkr", Stable, &["zicsr"]),
     ("zks", Stable, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]),
     ("zksed", Stable, &[]),
     ("zksh", Stable, &[]),
@@ -533,7 +544,7 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     ("zvbb", Unstable(sym::riscv_target_feature), &["zvkb"]),
     ("zvbc", Unstable(sym::riscv_target_feature), &["zve64x"]),
     ("zve32f", Unstable(sym::riscv_target_feature), &["zve32x", "f"]),
-    ("zve32x", Unstable(sym::riscv_target_feature), &["zvl32b"]),
+    ("zve32x", Unstable(sym::riscv_target_feature), &["zvl32b", "zicsr"]),
     ("zve64d", Unstable(sym::riscv_target_feature), &["zve64f", "d"]),
     ("zve64f", Unstable(sym::riscv_target_feature), &["zve32f", "zve64x"]),
     ("zve64x", Unstable(sym::riscv_target_feature), &["zve32x", "zvl64b"]),
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs
index 0bcb5f6f3b2..f45e3904212 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs
@@ -1051,7 +1051,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
                             None?
                         }
                         let args = self.node_args_opt(expr.hir_id)?;
-                        let span = tcx.hir().span(segment.hir_id);
+                        let span = tcx.hir_span(segment.hir_id);
                         let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
                         InsertableGenericArgs {
                             insert_span,
@@ -1110,7 +1110,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
                 if generics.has_impl_trait() {
                     return None;
                 }
-                let span = tcx.hir().span(segment.hir_id);
+                let span = tcx.hir_span(segment.hir_id);
                 let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
                 Some(InsertableGenericArgs {
                     insert_span,
@@ -1144,7 +1144,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
                     if !segment.infer_args || generics.has_impl_trait() {
                         do yeet ();
                     }
-                    let span = tcx.hir().span(segment.hir_id);
+                    let span = tcx.hir_span(segment.hir_id);
                     let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
                     InsertableGenericArgs {
                         insert_span,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
index 083ce022238..3559c660ee2 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
@@ -365,7 +365,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
                 // obligation comes from the `impl`. Find that `impl` so that we can point
                 // at it in the suggestion.
                 let trait_did = trait_id.to_def_id();
-                tcx.hir_trait_impls(trait_did).iter().find_map(|&impl_did| {
+                tcx.local_trait_impls(trait_did).iter().find_map(|&impl_did| {
                     if let Node::Item(Item {
                         kind: ItemKind::Impl(hir::Impl { self_ty, .. }), ..
                     }) = tcx.hir_node_by_def_id(impl_did)
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs
index 00f053fa599..683b5b528c6 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs
@@ -51,7 +51,6 @@ pub fn find_param_with_region<'tcx>(
         _ => return None, // not a free region
     };
 
-    let hir = &tcx.hir();
     let def_id = id.as_local()?;
 
     // FIXME: use def_kind
@@ -93,7 +92,7 @@ pub fn find_param_with_region<'tcx>(
             });
             found_anon_region.then(|| {
                 let ty_hir_id = fn_decl.inputs[index].hir_id;
-                let param_ty_span = hir.span(ty_hir_id);
+                let param_ty_span = tcx.hir_span(ty_hir_id);
                 let is_first = index == 0;
                 AnonymousParamInfo { param, param_ty: new_param_ty, param_ty_span, kind, is_first }
             })
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 07a67cde3be..73ae5177c48 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -1236,7 +1236,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 );
                 // Only suggest derive if this isn't a derived obligation,
                 // and the struct is local.
-                if let Some(span) = self.tcx.hir().span_if_local(def.did())
+                if let Some(span) = self.tcx.hir_span_if_local(def.did())
                     && obligation.cause.code().parent().is_none()
                 {
                     if ty.is_structural_eq_shallow(self.tcx) {
@@ -2943,7 +2943,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         };
 
         let found_node = found_did.and_then(|did| self.tcx.hir_get_if_local(did));
-        let found_span = found_did.and_then(|did| self.tcx.hir().span_if_local(did));
+        let found_span = found_did.and_then(|did| self.tcx.hir_span_if_local(did));
 
         if !self.reported_signature_mismatch.borrow_mut().insert((span, found_span)) {
             // We check closures twice, with obligations flowing in different directions,
@@ -3030,7 +3030,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         node: Node<'_>,
     ) -> Option<(Span, Option<Span>, Vec<ArgKind>)> {
         let sm = self.tcx.sess.source_map();
-        let hir = self.tcx.hir();
         Some(match node {
             Node::Expr(&hir::Expr {
                 kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }),
@@ -3086,7 +3085,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     .collect::<Vec<ArgKind>>(),
             ),
             Node::Ctor(variant_data) => {
-                let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id));
+                let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| self.tcx.hir_span(id));
                 (span, None, vec![ArgKind::empty(); variant_data.fields().len()])
             }
             _ => panic!("non-FnLike node found: {node:?}"),
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
index 98df09b6f7b..efee6e2aa1d 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
@@ -398,7 +398,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         );
 
         if !self.tcx.is_impl_trait_in_trait(trait_item_def_id) {
-            if let Some(span) = self.tcx.hir().span_if_local(trait_item_def_id) {
+            if let Some(span) = self.tcx.hir_span_if_local(trait_item_def_id) {
                 let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
                 err.span_label(span, format!("definition of `{item_name}` from trait"));
             }
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index dc8022b95c3..38fcba4ea62 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -1193,7 +1193,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             // FIXME(compiler-errors): This is kind of a mess, but required for obligations
             // that come from a path expr to affect the *call* expr.
             c @ ObligationCauseCode::WhereClauseInExpr(_, _, hir_id, _)
-                if self.tcx.hir().span(*hir_id).lo() == span.lo() =>
+                if self.tcx.hir_span(*hir_id).lo() == span.lo() =>
             {
                 c
             }
@@ -4481,7 +4481,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             Obligation::new(self.tcx, obligation.cause.clone(), obligation.param_env, trait_ref);
 
         if self.predicate_must_hold_modulo_regions(&obligation) {
-            let arg_span = self.tcx.hir().span(*arg_hir_id);
+            let arg_span = self.tcx.hir_span(*arg_hir_id);
             err.multipart_suggestion_verbose(
                 format!("use a unary tuple instead"),
                 vec![(arg_span.shrink_to_lo(), "(".into()), (arg_span.shrink_to_hi(), ",)".into())],
@@ -4521,7 +4521,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     parent_code: _,
                 } = cause.code()
                 {
-                    let arg_span = self.tcx.hir().span(*arg_hir_id);
+                    let arg_span = self.tcx.hir_span(*arg_hir_id);
                     let mut sp: MultiSpan = arg_span.into();
 
                     sp.push_span_label(
@@ -4530,7 +4530,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         generic types that should be inferred from this argument",
                     );
                     sp.push_span_label(
-                        self.tcx.hir().span(*call_hir_id),
+                        self.tcx.hir_span(*call_hir_id),
                         "add turbofish arguments to this call to \
                         specify the types manually, even if it's redundant",
                     );
@@ -4939,7 +4939,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         .type_implements_trait(pred.def_id(), [rhs_ty, lhs_ty], param_env)
                         .must_apply_modulo_regions()
                 {
-                    let lhs_span = tcx.hir().span(lhs_hir_id);
+                    let lhs_span = tcx.hir_span(lhs_hir_id);
                     let sm = tcx.sess.source_map();
                     if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_span)
                         && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_span)
diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs
index 3d9a90eb74e..f2725411e13 100644
--- a/compiler/rustc_trait_selection/src/solve/delegate.rs
+++ b/compiler/rustc_trait_selection/src/solve/delegate.rs
@@ -149,16 +149,16 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
         self.0.instantiate_canonical_var(span, cv_info, universe_map)
     }
 
-    fn insert_hidden_type(
+    fn register_hidden_type_in_storage(
         &self,
-        opaque_type_key: ty::OpaqueTypeKey<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        hidden_ty: Ty<'tcx>,
-        goals: &mut Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
-    ) -> Result<(), NoSolution> {
-        self.0
-            .insert_hidden_type(opaque_type_key, DUMMY_SP, param_env, hidden_ty, goals)
-            .map_err(|_| NoSolution)
+        opaque_type_key: rustc_type_ir::OpaqueTypeKey<Self::Interner>,
+        hidden_ty: <Self::Interner as ty::Interner>::Ty,
+        span: <Self::Interner as ty::Interner>::Span,
+    ) -> Option<<Self::Interner as ty::Interner>::Ty> {
+        self.0.register_hidden_type_in_storage(
+            opaque_type_key,
+            ty::OpaqueHiddenType { span, ty: hidden_ty },
+        )
     }
 
     fn add_item_bounds_for_hidden_type(
@@ -172,15 +172,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
         self.0.add_item_bounds_for_hidden_type(def_id, args, param_env, hidden_ty, goals);
     }
 
-    fn inject_new_hidden_type_unchecked(
-        &self,
-        key: ty::OpaqueTypeKey<'tcx>,
-        hidden_ty: Ty<'tcx>,
-        span: Span,
-    ) {
-        self.0.inject_new_hidden_type_unchecked(key, ty::OpaqueHiddenType { ty: hidden_ty, span })
-    }
-
     fn reset_opaque_types(&self) {
         let _ = self.take_opaque_types();
     }
diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
index 78a45243983..fa6bbf1d6e5 100644
--- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
+++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
@@ -583,27 +583,36 @@ fn receiver_is_dispatchable<'tcx>(
     // create a modified param env, with `Self: Unsize<U>` and `U: Trait` (and all of
     // its supertraits) added to caller bounds. `U: ?Sized` is already implied here.
     let param_env = {
-        let param_env = tcx.param_env(method.def_id);
+        // N.B. We generally want to emulate the construction of the `unnormalized_param_env`
+        // in the param-env query here. The fact that we don't just start with the clauses
+        // in the param-env of the method is because those are already normalized, and mixing
+        // normalized and unnormalized copies of predicates in `normalize_param_env_or_error`
+        // will cause ambiguity that the user can't really avoid.
+        //
+        // We leave out certain complexities of the param-env query here. Specifically, we:
+        // 1. Do not add `~const` bounds since there are no `dyn const Trait`s.
+        // 2. Do not add RPITIT self projection bounds for defaulted methods, since we
+        //    are not constructing a param-env for "inside" of the body of the defaulted
+        //    method, so we don't really care about projecting to a specific RPIT type,
+        //    and because RPITITs are not dyn compatible (yet).
+        let mut predicates = tcx.predicates_of(method.def_id).instantiate_identity(tcx).predicates;
 
         // Self: Unsize<U>
         let unsize_predicate =
-            ty::TraitRef::new(tcx, unsize_did, [tcx.types.self_param, unsized_self_ty]).upcast(tcx);
+            ty::TraitRef::new(tcx, unsize_did, [tcx.types.self_param, unsized_self_ty]);
+        predicates.push(unsize_predicate.upcast(tcx));
 
         // U: Trait<Arg1, ..., ArgN>
-        let trait_predicate = {
-            let trait_def_id = method.trait_container(tcx).unwrap();
-            let args = GenericArgs::for_item(tcx, trait_def_id, |param, _| {
-                if param.index == 0 { unsized_self_ty.into() } else { tcx.mk_param_from_def(param) }
-            });
-
-            ty::TraitRef::new_from_args(tcx, trait_def_id, args).upcast(tcx)
-        };
+        let trait_def_id = method.trait_container(tcx).unwrap();
+        let args = GenericArgs::for_item(tcx, trait_def_id, |param, _| {
+            if param.index == 0 { unsized_self_ty.into() } else { tcx.mk_param_from_def(param) }
+        });
+        let trait_predicate = ty::TraitRef::new_from_args(tcx, trait_def_id, args);
+        predicates.push(trait_predicate.upcast(tcx));
 
         normalize_param_env_or_error(
             tcx,
-            ty::ParamEnv::new(tcx.mk_clauses_from_iter(
-                param_env.caller_bounds().iter().chain([unsize_predicate, trait_predicate]),
-            )),
+            ty::ParamEnv::new(tcx.mk_clauses(&predicates)),
             ObligationCause::dummy_with_span(tcx.def_span(method.def_id)),
         )
     };
diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs
index b8e15088853..defbafac20b 100644
--- a/compiler/rustc_trait_selection/src/traits/effects.rs
+++ b/compiler/rustc_trait_selection/src/traits/effects.rs
@@ -259,7 +259,7 @@ fn evaluate_host_effect_for_destruct_goal<'tcx>(
                 .all_fields()
                 .map(|field| ty::TraitRef::new(tcx, destruct_def_id, [field.ty(tcx, args)]))
                 .collect();
-            match adt_def.destructor(tcx).map(|dtor| dtor.constness) {
+            match adt_def.destructor(tcx).map(|dtor| tcx.constness(dtor.did)) {
                 // `Drop` impl exists, but it's not const. Type cannot be `~const Destruct`.
                 Some(hir::Constness::NotConst) => return Err(EvaluationFailure::NoSolution),
                 // `Drop` impl exists, and it's const. Require `Ty: ~const Drop` to hold.
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 d15c9afef3a..cf6d2bc151f 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -692,6 +692,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         let def_id = obligation.predicate.def_id();
 
+        let mut check_impls = || {
+            // Only consider auto impls if there are no manual impls for the root of `self_ty`.
+            //
+            // For example, we only consider auto candidates for `&i32: Auto` if no explicit impl
+            // for `&SomeType: Auto` exists. Due to E0321 the only crate where impls
+            // for `&SomeType: Auto` can be defined is the crate where `Auto` has been defined.
+            //
+            // Generally, we have to guarantee that for all `SimplifiedType`s the only crate
+            // which may define impls for that type is either the crate defining the type
+            // or the trait. This should be guaranteed by the orphan check.
+            let mut has_impl = false;
+            self.tcx().for_each_relevant_impl(def_id, self_ty, |_| has_impl = true);
+            if !has_impl {
+                candidates.vec.push(AutoImplCandidate)
+            }
+        };
+
         if self.tcx().trait_is_auto(def_id) {
             match *self_ty.kind() {
                 ty::Dynamic(..) => {
@@ -705,6 +722,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     // we don't add any `..` impl. Default traits could
                     // still be provided by a manual implementation for
                     // this trait and type.
+
+                    // Backward compatibility for default auto traits.
+                    // Test: ui/traits/default_auto_traits/extern-types.rs
+                    if self.tcx().is_default_trait(def_id) {
+                        check_impls()
+                    }
                 }
                 ty::Param(..)
                 | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
@@ -805,20 +828,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         return;
                     }
 
-                    // Only consider auto impls if there are no manual impls for the root of `self_ty`.
-                    //
-                    // For example, we only consider auto candidates for `&i32: Auto` if no explicit impl
-                    // for `&SomeType: Auto` exists. Due to E0321 the only crate where impls
-                    // for `&SomeType: Auto` can be defined is the crate where `Auto` has been defined.
-                    //
-                    // Generally, we have to guarantee that for all `SimplifiedType`s the only crate
-                    // which may define impls for that type is either the crate defining the type
-                    // or the trait. This should be guaranteed by the orphan check.
-                    let mut has_impl = false;
-                    self.tcx().for_each_relevant_impl(def_id, self_ty, |_| has_impl = true);
-                    if !has_impl {
-                        candidates.vec.push(AutoImplCandidate)
-                    }
+                    check_impls();
                 }
                 ty::Error(_) => {
                     candidates.vec.push(AutoImplCandidate);
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 0679dbf1296..42c598e538d 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -2299,6 +2299,11 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             | ty::Never
             | ty::Char => ty::Binder::dummy(Vec::new()),
 
+            // This branch is only for `experimental_default_bounds`.
+            // Other foreign types were rejected earlier in
+            // `assemble_candidates_from_auto_impls`.
+            ty::Foreign(..) => ty::Binder::dummy(Vec::new()),
+
             // FIXME(unsafe_binders): Squash the double binder for now, I guess.
             ty::UnsafeBinder(_) => return Err(SelectionError::Unimplemented),
 
@@ -2308,7 +2313,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             ty::Placeholder(..)
             | ty::Dynamic(..)
             | ty::Param(..)
-            | ty::Foreign(..)
             | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
             | ty::Bound(..)
             | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index 8f86270d7dc..ff0d8cdd585 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -259,6 +259,8 @@ pub trait Interner:
 
     fn is_lang_item(self, def_id: Self::DefId, lang_item: TraitSolverLangItem) -> bool;
 
+    fn is_default_trait(self, def_id: Self::DefId) -> bool;
+
     fn as_lang_item(self, def_id: Self::DefId) -> Option<TraitSolverLangItem>;
 
     fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator<Item = Self::DefId>;