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.rs135
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs32
-rw-r--r--compiler/rustc_ast/src/util/parser.rs115
-rw-r--r--compiler/rustc_ast/src/visit.rs20
-rw-r--r--compiler/rustc_ast_lowering/src/index.rs13
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs46
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs18
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs32
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs4
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs4
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/fixup.rs2
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/item.rs11
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs14
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs25
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs28
-rw-r--r--compiler/rustc_const_eval/src/check_consts/check.rs3
-rw-r--r--compiler/rustc_const_eval/src/interpret/stack.rs2
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs4
-rw-r--r--compiler/rustc_hir/src/hir.rs160
-rw-r--r--compiler/rustc_hir/src/intravisit.rs14
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs21
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs15
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs20
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs85
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs53
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs16
-rw-r--r--compiler/rustc_interface/src/tests.rs2
-rw-r--r--compiler/rustc_lexer/src/lib.rs8
-rw-r--r--compiler/rustc_lexer/src/tests.rs72
-rw-r--r--compiler/rustc_lint/src/builtin.rs30
-rw-r--r--compiler/rustc_lint/src/lints.rs2
-rw-r--r--compiler/rustc_lint/src/unused.rs6
-rw-r--r--compiler/rustc_metadata/src/creader.rs2
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs4
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs4
-rw-r--r--compiler/rustc_middle/src/ty/context.rs4
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs4
-rw-r--r--compiler/rustc_middle/src/ty/error.rs9
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs5
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs4
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/cursor.rs65
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/direction.rs294
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/fmt.rs13
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/graphviz.rs8
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/lattice.rs108
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/mod.rs31
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/results.rs39
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/visitor.rs4
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs6
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/initialized.rs146
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/mod.rs7
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs29
-rw-r--r--compiler/rustc_mir_dataflow/src/lib.rs5
-rw-r--r--compiler/rustc_mir_dataflow/src/rustc_peek.rs11
-rw-r--r--compiler/rustc_mir_dataflow/src/storage.rs20
-rw-r--r--compiler/rustc_mir_transform/src/coroutine.rs16
-rw-r--r--compiler/rustc_mir_transform/src/dest_prop.rs5
-rw-r--r--compiler/rustc_mir_transform/src/early_otherwise_branch.rs4
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs6
-rw-r--r--compiler/rustc_mir_transform/src/instsimplify.rs71
-rw-r--r--compiler/rustc_mir_transform/src/lint.rs3
-rw-r--r--compiler/rustc_mir_transform/src/match_branches.rs5
-rw-r--r--compiler/rustc_mir_transform/src/multiple_return_terminators.rs7
-rw-r--r--compiler/rustc_mir_transform/src/nrvo.rs4
-rw-r--r--compiler/rustc_mir_transform/src/ref_prop.rs3
-rw-r--r--compiler/rustc_mir_transform/src/remove_unneeded_drops.rs5
-rw-r--r--compiler/rustc_mir_transform/src/remove_zsts.rs12
-rw-r--r--compiler/rustc_mir_transform/src/unreachable_prop.rs6
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs75
-rw-r--r--compiler/rustc_parse/src/lexer/tokentrees.rs83
-rw-r--r--compiler/rustc_parse/src/lexer/unicode_chars.rs8
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs62
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs17
-rw-r--r--compiler/rustc_passes/src/input_stats.rs11
-rw-r--r--compiler/rustc_resolve/src/late.rs13
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs37
-rw-r--r--compiler/rustc_session/messages.ftl2
-rw-r--r--compiler/rustc_session/src/config.rs8
-rw-r--r--compiler/rustc_session/src/errors.rs6
-rw-r--r--compiler/rustc_session/src/options.rs20
-rw-r--r--compiler/rustc_session/src/session.rs59
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_target/src/spec/tests/tests_impl.rs28
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs13
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs18
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs24
-rw-r--r--compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs43
-rw-r--r--compiler/rustc_type_ir/src/error.rs4
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs2
-rw-r--r--compiler/rustc_type_ir/src/relate.rs15
98 files changed, 1008 insertions, 1550 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 792de77e9d4..ecb6ef5a2a6 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -39,7 +39,9 @@ pub use crate::format::*;
 use crate::ptr::P;
 use crate::token::{self, CommentKind, Delimiter};
 use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream};
-pub use crate::util::parser::ExprPrecedence;
+use crate::util::parser::{
+    AssocOp, PREC_CLOSURE, PREC_JUMP, PREC_PREFIX, PREC_RANGE, PREC_UNAMBIGUOUS,
+};
 
 /// A "Label" is an identifier of some point in sources,
 /// e.g. in the following code:
@@ -428,7 +430,15 @@ impl Default for WhereClause {
 
 /// A single predicate in a where-clause.
 #[derive(Clone, Encodable, Decodable, Debug)]
-pub enum WherePredicate {
+pub struct WherePredicate {
+    pub kind: WherePredicateKind,
+    pub id: NodeId,
+    pub span: Span,
+}
+
+/// Predicate kind in where-clause.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum WherePredicateKind {
     /// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`).
     BoundPredicate(WhereBoundPredicate),
     /// A lifetime predicate (e.g., `'a: 'b + 'c`).
@@ -437,22 +447,11 @@ pub enum WherePredicate {
     EqPredicate(WhereEqPredicate),
 }
 
-impl WherePredicate {
-    pub fn span(&self) -> Span {
-        match self {
-            WherePredicate::BoundPredicate(p) => p.span,
-            WherePredicate::RegionPredicate(p) => p.span,
-            WherePredicate::EqPredicate(p) => p.span,
-        }
-    }
-}
-
 /// A type bound.
 ///
 /// E.g., `for<'c> Foo: Send + Clone + 'c`.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct WhereBoundPredicate {
-    pub span: Span,
     /// Any generics from a `for` binding.
     pub bound_generic_params: ThinVec<GenericParam>,
     /// The type being bounded.
@@ -466,7 +465,6 @@ pub struct WhereBoundPredicate {
 /// E.g., `'a: 'b + 'c`.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct WhereRegionPredicate {
-    pub span: Span,
     pub lifetime: Lifetime,
     pub bounds: GenericBounds,
 }
@@ -476,7 +474,6 @@ pub struct WhereRegionPredicate {
 /// E.g., `T = int`.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct WhereEqPredicate {
-    pub span: Span,
     pub lhs_ty: P<Ty>,
     pub rhs_ty: P<Ty>,
 }
@@ -1319,53 +1316,71 @@ impl Expr {
         Some(P(Ty { kind, id: self.id, span: self.span, tokens: None }))
     }
 
-    pub fn precedence(&self) -> ExprPrecedence {
+    pub fn precedence(&self) -> i8 {
         match self.kind {
-            ExprKind::Array(_) => ExprPrecedence::Array,
-            ExprKind::ConstBlock(_) => ExprPrecedence::ConstBlock,
-            ExprKind::Call(..) => ExprPrecedence::Call,
-            ExprKind::MethodCall(..) => ExprPrecedence::MethodCall,
-            ExprKind::Tup(_) => ExprPrecedence::Tup,
-            ExprKind::Binary(op, ..) => ExprPrecedence::Binary(op.node),
-            ExprKind::Unary(..) => ExprPrecedence::Unary,
-            ExprKind::Lit(_) | ExprKind::IncludedBytes(..) => ExprPrecedence::Lit,
-            ExprKind::Cast(..) => ExprPrecedence::Cast,
-            ExprKind::Let(..) => ExprPrecedence::Let,
-            ExprKind::If(..) => ExprPrecedence::If,
-            ExprKind::While(..) => ExprPrecedence::While,
-            ExprKind::ForLoop { .. } => ExprPrecedence::ForLoop,
-            ExprKind::Loop(..) => ExprPrecedence::Loop,
-            ExprKind::Match(_, _, MatchKind::Prefix) => ExprPrecedence::Match,
-            ExprKind::Match(_, _, MatchKind::Postfix) => ExprPrecedence::PostfixMatch,
-            ExprKind::Closure(..) => ExprPrecedence::Closure,
-            ExprKind::Block(..) => ExprPrecedence::Block,
-            ExprKind::TryBlock(..) => ExprPrecedence::TryBlock,
-            ExprKind::Gen(..) => ExprPrecedence::Gen,
-            ExprKind::Await(..) => ExprPrecedence::Await,
-            ExprKind::Assign(..) => ExprPrecedence::Assign,
-            ExprKind::AssignOp(..) => ExprPrecedence::AssignOp,
-            ExprKind::Field(..) => ExprPrecedence::Field,
-            ExprKind::Index(..) => ExprPrecedence::Index,
-            ExprKind::Range(..) => ExprPrecedence::Range,
-            ExprKind::Underscore => ExprPrecedence::Path,
-            ExprKind::Path(..) => ExprPrecedence::Path,
-            ExprKind::AddrOf(..) => ExprPrecedence::AddrOf,
-            ExprKind::Break(..) => ExprPrecedence::Break,
-            ExprKind::Continue(..) => ExprPrecedence::Continue,
-            ExprKind::Ret(..) => ExprPrecedence::Ret,
-            ExprKind::Struct(..) => ExprPrecedence::Struct,
-            ExprKind::Repeat(..) => ExprPrecedence::Repeat,
-            ExprKind::Paren(..) => ExprPrecedence::Paren,
-            ExprKind::Try(..) => ExprPrecedence::Try,
-            ExprKind::Yield(..) => ExprPrecedence::Yield,
-            ExprKind::Yeet(..) => ExprPrecedence::Yeet,
-            ExprKind::Become(..) => ExprPrecedence::Become,
-            ExprKind::InlineAsm(..)
-            | ExprKind::Type(..)
-            | ExprKind::OffsetOf(..)
+            ExprKind::Closure(..) => PREC_CLOSURE,
+
+            ExprKind::Break(..)
+            | ExprKind::Continue(..)
+            | ExprKind::Ret(..)
+            | ExprKind::Yield(..)
+            | ExprKind::Yeet(..)
+            | ExprKind::Become(..) => PREC_JUMP,
+
+            // `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
+            // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence
+            // ensures that `pprust` will add parentheses in the right places to get the desired
+            // parse.
+            ExprKind::Range(..) => PREC_RANGE,
+
+            // Binop-like expr kinds, handled by `AssocOp`.
+            ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence() as i8,
+            ExprKind::Cast(..) => AssocOp::As.precedence() as i8,
+
+            ExprKind::Assign(..) |
+            ExprKind::AssignOp(..) => AssocOp::Assign.precedence() as i8,
+
+            // Unary, prefix
+            ExprKind::AddrOf(..)
+            // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`.
+            // However, this is not exactly right. When `let _ = a` is the LHS of a binop we
+            // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b`
+            // but we need to print `(let _ = a) < b` as-is with parens.
+            | ExprKind::Let(..)
+            | ExprKind::Unary(..) => PREC_PREFIX,
+
+            // Never need parens
+            ExprKind::Array(_)
+            | ExprKind::Await(..)
+            | ExprKind::Block(..)
+            | ExprKind::Call(..)
+            | ExprKind::ConstBlock(_)
+            | ExprKind::Field(..)
+            | ExprKind::ForLoop { .. }
             | ExprKind::FormatArgs(..)
-            | ExprKind::MacCall(..) => ExprPrecedence::Mac,
-            ExprKind::Err(_) | ExprKind::Dummy => ExprPrecedence::Err,
+            | ExprKind::Gen(..)
+            | ExprKind::If(..)
+            | ExprKind::IncludedBytes(..)
+            | ExprKind::Index(..)
+            | ExprKind::InlineAsm(..)
+            | ExprKind::Lit(_)
+            | ExprKind::Loop(..)
+            | ExprKind::MacCall(..)
+            | ExprKind::Match(..)
+            | ExprKind::MethodCall(..)
+            | ExprKind::OffsetOf(..)
+            | ExprKind::Paren(..)
+            | ExprKind::Path(..)
+            | ExprKind::Repeat(..)
+            | ExprKind::Struct(..)
+            | ExprKind::Try(..)
+            | ExprKind::TryBlock(..)
+            | ExprKind::Tup(_)
+            | ExprKind::Type(..)
+            | ExprKind::Underscore
+            | ExprKind::While(..)
+            | ExprKind::Err(_)
+            | ExprKind::Dummy => PREC_UNAMBIGUOUS,
         }
     }
 
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 198e1bca774..0aceed45028 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -332,7 +332,11 @@ pub trait MutVisitor: Sized {
     }
 
     fn visit_where_predicate(&mut self, where_predicate: &mut WherePredicate) {
-        walk_where_predicate(self, where_predicate);
+        walk_where_predicate(self, where_predicate)
+    }
+
+    fn visit_where_predicate_kind(&mut self, kind: &mut WherePredicateKind) {
+        walk_where_predicate_kind(self, kind)
     }
 
     fn visit_vis(&mut self, vis: &mut Visibility) {
@@ -1065,26 +1069,30 @@ fn walk_where_clause<T: MutVisitor>(vis: &mut T, wc: &mut WhereClause) {
     vis.visit_span(span);
 }
 
-fn walk_where_predicate<T: MutVisitor>(vis: &mut T, pred: &mut WherePredicate) {
-    match pred {
-        WherePredicate::BoundPredicate(bp) => {
-            let WhereBoundPredicate { span, bound_generic_params, bounded_ty, bounds } = bp;
+pub fn walk_where_predicate<T: MutVisitor>(vis: &mut T, pred: &mut WherePredicate) {
+    let WherePredicate { kind, id, span } = pred;
+    vis.visit_id(id);
+    vis.visit_where_predicate_kind(kind);
+    vis.visit_span(span);
+}
+
+pub fn walk_where_predicate_kind<T: MutVisitor>(vis: &mut T, kind: &mut WherePredicateKind) {
+    match kind {
+        WherePredicateKind::BoundPredicate(bp) => {
+            let WhereBoundPredicate { bound_generic_params, bounded_ty, bounds } = bp;
             bound_generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
             vis.visit_ty(bounded_ty);
             visit_vec(bounds, |bound| vis.visit_param_bound(bound, BoundKind::Bound));
-            vis.visit_span(span);
         }
-        WherePredicate::RegionPredicate(rp) => {
-            let WhereRegionPredicate { span, lifetime, bounds } = rp;
+        WherePredicateKind::RegionPredicate(rp) => {
+            let WhereRegionPredicate { lifetime, bounds } = rp;
             vis.visit_lifetime(lifetime);
             visit_vec(bounds, |bound| vis.visit_param_bound(bound, BoundKind::Bound));
-            vis.visit_span(span);
         }
-        WherePredicate::EqPredicate(ep) => {
-            let WhereEqPredicate { span, lhs_ty, rhs_ty } = ep;
+        WherePredicateKind::EqPredicate(ep) => {
+            let WhereEqPredicate { lhs_ty, rhs_ty } = ep;
             vis.visit_ty(lhs_ty);
             vis.visit_ty(rhs_ty);
-            vis.visit_span(span);
         }
     }
 }
diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs
index d8dad4550c0..ed9265d5159 100644
--- a/compiler/rustc_ast/src/util/parser.rs
+++ b/compiler/rustc_ast/src/util/parser.rs
@@ -237,121 +237,6 @@ pub const PREC_PREFIX: i8 = 50;
 pub const PREC_UNAMBIGUOUS: i8 = 60;
 pub const PREC_FORCE_PAREN: i8 = 100;
 
-#[derive(Debug, Clone, Copy)]
-pub enum ExprPrecedence {
-    Closure,
-    Break,
-    Continue,
-    Ret,
-    Yield,
-    Yeet,
-    Become,
-
-    Range,
-
-    Binary(BinOpKind),
-
-    Cast,
-
-    Assign,
-    AssignOp,
-
-    AddrOf,
-    Let,
-    Unary,
-
-    Call,
-    MethodCall,
-    Field,
-    Index,
-    Try,
-    Mac,
-
-    Array,
-    Repeat,
-    Tup,
-    Lit,
-    Path,
-    Paren,
-    If,
-    While,
-    ForLoop,
-    Loop,
-    Match,
-    PostfixMatch,
-    ConstBlock,
-    Block,
-    TryBlock,
-    Struct,
-    Gen,
-    Await,
-    Err,
-}
-
-impl ExprPrecedence {
-    pub fn order(self) -> i8 {
-        match self {
-            ExprPrecedence::Closure => PREC_CLOSURE,
-
-            ExprPrecedence::Break
-            | ExprPrecedence::Continue
-            | ExprPrecedence::Ret
-            | ExprPrecedence::Yield
-            | ExprPrecedence::Yeet
-            | ExprPrecedence::Become => PREC_JUMP,
-
-            // `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
-            // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence
-            // ensures that `pprust` will add parentheses in the right places to get the desired
-            // parse.
-            ExprPrecedence::Range => PREC_RANGE,
-
-            // Binop-like expr kinds, handled by `AssocOp`.
-            ExprPrecedence::Binary(op) => AssocOp::from_ast_binop(op).precedence() as i8,
-            ExprPrecedence::Cast => AssocOp::As.precedence() as i8,
-
-            ExprPrecedence::Assign |
-            ExprPrecedence::AssignOp => AssocOp::Assign.precedence() as i8,
-
-            // Unary, prefix
-            ExprPrecedence::AddrOf
-            // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`.
-            // However, this is not exactly right. When `let _ = a` is the LHS of a binop we
-            // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b`
-            // but we need to print `(let _ = a) < b` as-is with parens.
-            | ExprPrecedence::Let
-            | ExprPrecedence::Unary => PREC_PREFIX,
-
-            // Never need parens
-            ExprPrecedence::Array
-            | ExprPrecedence::Await
-            | ExprPrecedence::Block
-            | ExprPrecedence::Call
-            | ExprPrecedence::ConstBlock
-            | ExprPrecedence::Field
-            | ExprPrecedence::ForLoop
-            | ExprPrecedence::Gen
-            | ExprPrecedence::If
-            | ExprPrecedence::Index
-            | ExprPrecedence::Lit
-            | ExprPrecedence::Loop
-            | ExprPrecedence::Mac
-            | ExprPrecedence::Match
-            | ExprPrecedence::MethodCall
-            | ExprPrecedence::Paren
-            | ExprPrecedence::Path
-            | ExprPrecedence::PostfixMatch
-            | ExprPrecedence::Repeat
-            | ExprPrecedence::Struct
-            | ExprPrecedence::Try
-            | ExprPrecedence::TryBlock
-            | ExprPrecedence::Tup
-            | ExprPrecedence::While
-            | ExprPrecedence::Err => PREC_UNAMBIGUOUS,
-        }
-    }
-}
-
 /// In `let p = e`, operators with precedence `<=` this one requires parentheses in `e`.
 pub fn prec_let_scrutinee_needs_par() -> usize {
     AssocOp::LAnd.precedence()
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 20ac9fa02bb..718397e8ca0 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -192,6 +192,9 @@ pub trait Visitor<'ast>: Sized {
     fn visit_where_predicate(&mut self, p: &'ast WherePredicate) -> Self::Result {
         walk_where_predicate(self, p)
     }
+    fn visit_where_predicate_kind(&mut self, k: &'ast WherePredicateKind) -> Self::Result {
+        walk_where_predicate_kind(self, k)
+    }
     fn visit_fn(&mut self, fk: FnKind<'ast>, _: Span, _: NodeId) -> Self::Result {
         walk_fn(self, fk)
     }
@@ -794,22 +797,29 @@ pub fn walk_where_predicate<'a, V: Visitor<'a>>(
     visitor: &mut V,
     predicate: &'a WherePredicate,
 ) -> V::Result {
-    match predicate {
-        WherePredicate::BoundPredicate(WhereBoundPredicate {
+    let WherePredicate { kind, id: _, span: _ } = predicate;
+    visitor.visit_where_predicate_kind(kind)
+}
+
+pub fn walk_where_predicate_kind<'a, V: Visitor<'a>>(
+    visitor: &mut V,
+    kind: &'a WherePredicateKind,
+) -> V::Result {
+    match kind {
+        WherePredicateKind::BoundPredicate(WhereBoundPredicate {
             bounded_ty,
             bounds,
             bound_generic_params,
-            span: _,
         }) => {
             walk_list!(visitor, visit_generic_param, bound_generic_params);
             try_visit!(visitor.visit_ty(bounded_ty));
             walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
         }
-        WherePredicate::RegionPredicate(WhereRegionPredicate { lifetime, bounds, span: _ }) => {
+        WherePredicateKind::RegionPredicate(WhereRegionPredicate { lifetime, bounds }) => {
             try_visit!(visitor.visit_lifetime(lifetime, LifetimeCtxt::Bound));
             walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
         }
-        WherePredicate::EqPredicate(WhereEqPredicate { lhs_ty, rhs_ty, span: _ }) => {
+        WherePredicateKind::EqPredicate(WhereEqPredicate { lhs_ty, rhs_ty }) => {
             try_visit!(visitor.visit_ty(lhs_ty));
             try_visit!(visitor.visit_ty(rhs_ty));
         }
diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs
index 6289966561f..65e387de800 100644
--- a/compiler/rustc_ast_lowering/src/index.rs
+++ b/compiler/rustc_ast_lowering/src/index.rs
@@ -381,15 +381,10 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
     }
 
     fn visit_where_predicate(&mut self, predicate: &'hir WherePredicate<'hir>) {
-        match predicate {
-            WherePredicate::BoundPredicate(pred) => {
-                self.insert(pred.span, pred.hir_id, Node::WhereBoundPredicate(pred));
-                self.with_parent(pred.hir_id, |this| {
-                    intravisit::walk_where_predicate(this, predicate)
-                })
-            }
-            _ => intravisit::walk_where_predicate(self, predicate),
-        }
+        self.insert(predicate.span, predicate.hir_id, Node::WherePredicate(predicate));
+        self.with_parent(predicate.hir_id, |this| {
+            intravisit::walk_where_predicate(this, predicate)
+        });
     }
 
     fn visit_array_length(&mut self, len: &'hir ArrayLen<'hir>) {
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 48b42fa2e97..fb09f1c7fee 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -1401,7 +1401,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         // keep track of the Span info. Now, `<dyn HirTyLowerer>::add_implicit_sized_bound`
         // checks both param bounds and where clauses for `?Sized`.
         for pred in &generics.where_clause.predicates {
-            let WherePredicate::BoundPredicate(bound_pred) = pred else {
+            let WherePredicateKind::BoundPredicate(bound_pred) = &pred.kind else {
                 continue;
             };
             let compute_is_param = || {
@@ -1538,9 +1538,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
             }
         });
         let span = self.lower_span(span);
-
-        match kind {
-            GenericParamKind::Const { .. } => None,
+        let hir_id = self.next_id();
+        let kind = self.arena.alloc(match kind {
+            GenericParamKind::Const { .. } => return None,
             GenericParamKind::Type { .. } => {
                 let def_id = self.local_def_id(id).to_def_id();
                 let hir_id = self.next_id();
@@ -1555,38 +1555,36 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let ty_id = self.next_id();
                 let bounded_ty =
                     self.ty_path(ty_id, param_span, hir::QPath::Resolved(None, ty_path));
-                Some(hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
-                    hir_id: self.next_id(),
+                hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate {
                     bounded_ty: self.arena.alloc(bounded_ty),
                     bounds,
-                    span,
                     bound_generic_params: &[],
                     origin,
-                }))
+                })
             }
             GenericParamKind::Lifetime => {
                 let ident = self.lower_ident(ident);
                 let lt_id = self.next_node_id();
                 let lifetime = self.new_named_lifetime(id, lt_id, ident);
-                Some(hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
+                hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate {
                     lifetime,
-                    span,
                     bounds,
                     in_where_clause: false,
-                }))
+                })
             }
-        }
+        });
+        Some(hir::WherePredicate { hir_id, span, kind })
     }
 
     fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicate<'hir> {
-        match pred {
-            WherePredicate::BoundPredicate(WhereBoundPredicate {
+        let hir_id = self.lower_node_id(pred.id);
+        let span = self.lower_span(pred.span);
+        let kind = self.arena.alloc(match &pred.kind {
+            WherePredicateKind::BoundPredicate(WhereBoundPredicate {
                 bound_generic_params,
                 bounded_ty,
                 bounds,
-                span,
-            }) => hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
-                hir_id: self.next_id(),
+            }) => hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate {
                 bound_generic_params: self
                     .lower_generic_params(bound_generic_params, hir::GenericParamSource::Binder),
                 bounded_ty: self
@@ -1595,12 +1593,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     bounds,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
                 ),
-                span: self.lower_span(*span),
                 origin: PredicateOrigin::WhereClause,
             }),
-            WherePredicate::RegionPredicate(WhereRegionPredicate { lifetime, bounds, span }) => {
-                hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
-                    span: self.lower_span(*span),
+            WherePredicateKind::RegionPredicate(WhereRegionPredicate { lifetime, bounds }) => {
+                hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate {
                     lifetime: self.lower_lifetime(lifetime),
                     bounds: self.lower_param_bounds(
                         bounds,
@@ -1609,15 +1605,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     in_where_clause: true,
                 })
             }
-            WherePredicate::EqPredicate(WhereEqPredicate { lhs_ty, rhs_ty, span }) => {
-                hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
+            WherePredicateKind::EqPredicate(WhereEqPredicate { lhs_ty, rhs_ty }) => {
+                hir::WherePredicateKind::EqPredicate(hir::WhereEqPredicate {
                     lhs_ty: self
                         .lower_ty(lhs_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Bound)),
                     rhs_ty: self
                         .lower_ty(rhs_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Bound)),
-                    span: self.lower_span(*span),
                 })
             }
-        }
+        });
+        hir::WherePredicate { hir_id, span, kind }
     }
 }
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 0b2969a49ba..dae816663e0 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -2117,11 +2117,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             hir::ConstArgKind::Anon(ct)
         };
 
-        self.arena.alloc(hir::ConstArg {
-            hir_id: self.next_id(),
-            kind: ct_kind,
-            is_desugared_from_effects: false,
-        })
+        self.arena.alloc(hir::ConstArg { hir_id: self.next_id(), kind: ct_kind })
     }
 
     /// See [`hir::ConstArg`] for when to use this function vs
@@ -2163,19 +2159,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 None,
             );
 
-            return ConstArg {
-                hir_id: self.next_id(),
-                kind: hir::ConstArgKind::Path(qpath),
-                is_desugared_from_effects: false,
-            };
+            return ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Path(qpath) };
         }
 
         let lowered_anon = self.lower_anon_const_to_anon_const(anon);
-        ConstArg {
-            hir_id: self.next_id(),
-            kind: hir::ConstArgKind::Anon(lowered_anon),
-            is_desugared_from_effects: false,
-        }
+        ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Anon(lowered_anon) }
     }
 
     /// See [`hir::ConstArg`] for when to use this function vs
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 07a6f4e5ee7..64e91c91e2c 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -1200,14 +1200,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
         validate_generic_param_order(self.dcx(), &generics.params, generics.span);
 
         for predicate in &generics.where_clause.predicates {
-            if let WherePredicate::EqPredicate(predicate) = predicate {
-                deny_equality_constraints(self, predicate, generics);
+            let span = predicate.span;
+            if let WherePredicateKind::EqPredicate(predicate) = &predicate.kind {
+                deny_equality_constraints(self, predicate, span, generics);
             }
         }
         walk_list!(self, visit_generic_param, &generics.params);
         for predicate in &generics.where_clause.predicates {
-            match predicate {
-                WherePredicate::BoundPredicate(bound_pred) => {
+            match &predicate.kind {
+                WherePredicateKind::BoundPredicate(bound_pred) => {
                     // This is slightly complicated. Our representation for poly-trait-refs contains a single
                     // binder and thus we only allow a single level of quantification. However,
                     // the syntax of Rust permits quantification in two places in where clauses,
@@ -1504,9 +1505,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
 fn deny_equality_constraints(
     this: &AstValidator<'_>,
     predicate: &WhereEqPredicate,
+    predicate_span: Span,
     generics: &Generics,
 ) {
-    let mut err = errors::EqualityInWhere { span: predicate.span, assoc: None, assoc2: None };
+    let mut err = errors::EqualityInWhere { span: predicate_span, assoc: None, assoc2: None };
 
     // Given `<A as Foo>::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
     if let TyKind::Path(Some(qself), full_path) = &predicate.lhs_ty.kind
@@ -1550,7 +1552,7 @@ fn deny_equality_constraints(
                     }
                 }
                 err.assoc = Some(errors::AssociatedSuggestion {
-                    span: predicate.span,
+                    span: predicate_span,
                     ident: *ident,
                     param: param.ident,
                     path: pprust::path_to_string(&assoc_path),
@@ -1580,23 +1582,23 @@ fn deny_equality_constraints(
                     // We're removing th eonly where bound left, remove the whole thing.
                     generics.where_clause.span
                 } else {
-                    let mut span = predicate.span;
+                    let mut span = predicate_span;
                     let mut prev: 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() {
-                        if let WherePredicate::EqPredicate(pred) = pred
-                            && pred.span == predicate.span
+                        if let WherePredicateKind::EqPredicate(_) = pred.kind
+                            && pred.span == predicate_span
                         {
                             if let Some(next) = preds.peek() {
                                 // This is the first predicate, remove the trailing comma as well.
-                                span = span.with_hi(next.span().lo());
+                                span = span.with_hi(next.span.lo());
                             } else if let Some(prev) = prev {
                                 // Remove the previous comma as well.
                                 span = span.with_lo(prev.hi());
                             }
                         }
-                        prev = Some(pred.span());
+                        prev = Some(pred.span);
                     }
                     span
                 };
@@ -1613,8 +1615,8 @@ fn deny_equality_constraints(
     if let TyKind::Path(None, full_path) = &predicate.lhs_ty.kind {
         // Given `A: Foo, Foo::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
         for bounds in generics.params.iter().map(|p| &p.bounds).chain(
-            generics.where_clause.predicates.iter().filter_map(|pred| match pred {
-                WherePredicate::BoundPredicate(p) => Some(&p.bounds),
+            generics.where_clause.predicates.iter().filter_map(|pred| match &pred.kind {
+                WherePredicateKind::BoundPredicate(p) => Some(&p.bounds),
                 _ => None,
             }),
         ) {
@@ -1637,8 +1639,8 @@ fn deny_equality_constraints(
         // Given `A: Foo, A::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
         if let [potential_param, potential_assoc] = &full_path.segments[..] {
             for (ident, bounds) in generics.params.iter().map(|p| (p.ident, &p.bounds)).chain(
-                generics.where_clause.predicates.iter().filter_map(|pred| match pred {
-                    WherePredicate::BoundPredicate(p)
+                generics.where_clause.predicates.iter().filter_map(|pred| match &pred.kind {
+                    WherePredicateKind::BoundPredicate(p)
                         if let ast::TyKind::Path(None, path) = &p.bounded_ty.kind
                             && let [segment] = &path.segments[..] =>
                     {
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 8a392e4407b..8cdc7133cc0 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -345,8 +345,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
 
     fn visit_generics(&mut self, g: &'a ast::Generics) {
         for predicate in &g.where_clause.predicates {
-            match predicate {
-                ast::WherePredicate::BoundPredicate(bound_pred) => {
+            match &predicate.kind {
+                ast::WherePredicateKind::BoundPredicate(bound_pred) => {
                     // A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`).
                     self.check_late_bound_lifetime_defs(&bound_pred.bound_generic_params);
                 }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 893bfaf8f71..04ec135c428 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -59,7 +59,7 @@ impl<'a> State<'a> {
     }
 
     fn print_expr_maybe_paren(&mut self, expr: &ast::Expr, prec: i8, fixup: FixupContext) {
-        self.print_expr_cond_paren(expr, expr.precedence().order() < prec, fixup);
+        self.print_expr_cond_paren(expr, expr.precedence() < prec, fixup);
     }
 
     /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in
@@ -615,7 +615,7 @@ impl<'a> State<'a> {
                         expr,
                         // Parenthesize if required by precedence, or in the
                         // case of `break 'inner: loop { break 'inner 1 } + 1`
-                        expr.precedence().order() < parser::PREC_JUMP
+                        expr.precedence() < parser::PREC_JUMP
                             || (opt_label.is_none() && classify::leading_labeled_expr(expr)),
                         fixup.subsequent_subexpression(),
                     );
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs b/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs
index 50fd12a4e8b..6f5382ce61d 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs
@@ -191,6 +191,6 @@ impl FixupContext {
     ///     "let chain".
     pub(crate) fn needs_par_as_let_scrutinee(self, expr: &Expr) -> bool {
         self.parenthesize_exterior_struct_lit && parser::contains_exterior_struct_lit(expr)
-            || parser::needs_par_as_let_scrutinee(expr.precedence().order())
+            || parser::needs_par_as_let_scrutinee(expr.precedence())
     }
 }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
index 8279c66836c..1ae765c0130 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -726,11 +726,12 @@ impl<'a> State<'a> {
     }
 
     pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) {
-        match predicate {
-            ast::WherePredicate::BoundPredicate(where_bound_predicate) => {
+        let ast::WherePredicate { kind, id: _, span: _ } = predicate;
+        match kind {
+            ast::WherePredicateKind::BoundPredicate(where_bound_predicate) => {
                 self.print_where_bound_predicate(where_bound_predicate);
             }
-            ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
+            ast::WherePredicateKind::RegionPredicate(ast::WhereRegionPredicate {
                 lifetime,
                 bounds,
                 ..
@@ -742,7 +743,9 @@ impl<'a> State<'a> {
                     self.print_lifetime_bounds(bounds);
                 }
             }
-            ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => {
+            ast::WherePredicateKind::EqPredicate(ast::WhereEqPredicate {
+                lhs_ty, rhs_ty, ..
+            }) => {
                 self.print_type(lhs_ty);
                 self.space();
                 self.word_space("=");
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index bda96726738..92afad62aa4 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -1062,8 +1062,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                         && let spans = hir_generics
                             .predicates
                             .iter()
-                            .filter_map(|pred| match pred {
-                                hir::WherePredicate::BoundPredicate(pred) => Some(pred),
+                            .filter_map(|pred| match pred.kind {
+                                hir::WherePredicateKind::BoundPredicate(pred) => Some(pred),
                                 _ => None,
                             })
                             .filter(|pred| {
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index c38747f6675..6ea5c44dc01 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -5,7 +5,7 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::GenericBound::Trait;
 use rustc_hir::QPath::Resolved;
-use rustc_hir::WherePredicate::BoundPredicate;
+use rustc_hir::WherePredicateKind::BoundPredicate;
 use rustc_hir::def::Res::Def;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
@@ -236,7 +236,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         let mut hrtb_bounds = vec![];
         gat_id_and_generics.iter().flatten().for_each(|(gat_hir_id, generics)| {
             for pred in generics.predicates {
-                let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) = pred
+                let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) =
+                    pred.kind
                 else {
                     continue;
                 };
@@ -267,12 +268,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             };
             debug!(?generics_fn);
             generics_fn.predicates.iter().for_each(|predicate| {
-                let BoundPredicate(WhereBoundPredicate {
-                    span: bounded_span,
-                    bounded_ty,
-                    bounds,
-                    ..
-                }) = predicate
+                let BoundPredicate(WhereBoundPredicate { bounded_ty, bounds, .. }) = predicate.kind
                 else {
                     return;
                 };
@@ -287,7 +283,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                             .rfind(|param| param.def_id.to_def_id() == defid)
                             .is_some()
                     {
-                        suggestions.push((bounded_span.shrink_to_hi(), " + 'static".to_string()));
+                        suggestions.push((predicate.span.shrink_to_hi(), " + 'static".to_string()));
                     }
                 });
             });
diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
index 53e938ee216..8adb9a3f4b0 100644
--- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
@@ -288,19 +288,18 @@ pub(crate) fn expand_deriving_coerce_pointee(
     //
     // We should also write a few new `where` bounds from `#[pointee] T` to `__S`
     // as well as any bound that indirectly involves the `#[pointee] T` type.
-    for bound in &generics.where_clause.predicates {
-        if let ast::WherePredicate::BoundPredicate(bound) = bound {
+    for predicate in &generics.where_clause.predicates {
+        if let ast::WherePredicateKind::BoundPredicate(bound) = &predicate.kind {
             let mut substitution = TypeSubstitution {
                 from_name: pointee_ty_ident.name,
                 to_ty: &s_ty,
                 rewritten: false,
             };
-            let mut predicate = ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
-                span: bound.span,
-                bound_generic_params: bound.bound_generic_params.clone(),
-                bounded_ty: bound.bounded_ty.clone(),
-                bounds: bound.bounds.clone(),
-            });
+            let mut predicate = ast::WherePredicate {
+                kind: ast::WherePredicateKind::BoundPredicate(bound.clone()),
+                span: predicate.span,
+                id: ast::DUMMY_NODE_ID,
+            };
             substitution.visit_where_predicate(&mut predicate);
             if substitution.rewritten {
                 impl_generics.where_clause.predicates.push(predicate);
@@ -319,7 +318,7 @@ pub(crate) fn expand_deriving_coerce_pointee(
 
 fn contains_maybe_sized_bound_on_pointee(predicates: &[WherePredicate], pointee: Symbol) -> bool {
     for bound in predicates {
-        if let ast::WherePredicate::BoundPredicate(bound) = bound
+        if let ast::WherePredicateKind::BoundPredicate(bound) = &bound.kind
             && bound.bounded_ty.kind.is_simple_path().is_some_and(|name| name == pointee)
         {
             for bound in &bound.bounds {
@@ -385,8 +384,8 @@ impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> {
     }
 
     fn visit_where_predicate(&mut self, where_predicate: &mut ast::WherePredicate) {
-        match where_predicate {
-            rustc_ast::WherePredicate::BoundPredicate(bound) => {
+        match &mut where_predicate.kind {
+            rustc_ast::WherePredicateKind::BoundPredicate(bound) => {
                 bound
                     .bound_generic_params
                     .flat_map_in_place(|param| self.flat_map_generic_param(param));
@@ -395,8 +394,8 @@ impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> {
                     self.visit_param_bound(bound, BoundKind::Bound)
                 }
             }
-            rustc_ast::WherePredicate::RegionPredicate(_)
-            | rustc_ast::WherePredicate::EqPredicate(_) => {}
+            rustc_ast::WherePredicateKind::RegionPredicate(_)
+            | rustc_ast::WherePredicateKind::EqPredicate(_) => {}
         }
     }
 }
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 79c198ed2d0..f6eea0b21ca 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -690,25 +690,10 @@ impl<'a> TraitDef<'a> {
 
         // and similarly for where clauses
         where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| {
-            match clause {
-                ast::WherePredicate::BoundPredicate(wb) => {
-                    let span = wb.span.with_ctxt(ctxt);
-                    ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
-                        span,
-                        ..wb.clone()
-                    })
-                }
-                ast::WherePredicate::RegionPredicate(wr) => {
-                    let span = wr.span.with_ctxt(ctxt);
-                    ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
-                        span,
-                        ..wr.clone()
-                    })
-                }
-                ast::WherePredicate::EqPredicate(we) => {
-                    let span = we.span.with_ctxt(ctxt);
-                    ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { span, ..we.clone() })
-                }
+            ast::WherePredicate {
+                kind: clause.kind.clone(),
+                id: ast::DUMMY_NODE_ID,
+                span: clause.span.with_ctxt(ctxt),
             }
         }));
 
@@ -757,13 +742,14 @@ impl<'a> TraitDef<'a> {
 
                     if !bounds.is_empty() {
                         let predicate = ast::WhereBoundPredicate {
-                            span: self.span,
                             bound_generic_params: field_ty_param.bound_generic_params,
                             bounded_ty: field_ty_param.ty,
                             bounds,
                         };
 
-                        let predicate = ast::WherePredicate::BoundPredicate(predicate);
+                        let kind = ast::WherePredicateKind::BoundPredicate(predicate);
+                        let predicate =
+                            ast::WherePredicate { kind, id: ast::DUMMY_NODE_ID, span: self.span };
                         where_clause.predicates.push(predicate);
                     }
                 }
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index 9f21c864487..916929b6c0b 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -18,8 +18,7 @@ use rustc_middle::span_bug;
 use rustc_middle::ty::adjustment::PointerCoercion;
 use rustc_middle::ty::{self, Ty, TypeVisitableExt};
 use rustc_mir_dataflow::Analysis;
-use rustc_mir_dataflow::impls::MaybeStorageLive;
-use rustc_mir_dataflow::storage::always_storage_live_locals;
+use rustc_mir_dataflow::impls::{MaybeStorageLive, always_storage_live_locals};
 use rustc_span::{Span, Symbol, sym};
 use rustc_trait_selection::traits::{
     Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt,
diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs
index 8ce11c71b8b..a9ebf386617 100644
--- a/compiler/rustc_const_eval/src/interpret/stack.rs
+++ b/compiler/rustc_const_eval/src/interpret/stack.rs
@@ -10,7 +10,7 @@ use rustc_index::IndexVec;
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::{bug, mir};
-use rustc_mir_dataflow::storage::always_storage_live_locals;
+use rustc_mir_dataflow::impls::always_storage_live_locals;
 use rustc_span::Span;
 use tracing::{info_span, instrument, trace};
 
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index c270ce16726..256266d2965 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -472,10 +472,6 @@ fn run_compiler(
             linker.link(sess, codegen_backend)?
         }
 
-        if let Some(fuel) = sess.opts.unstable_opts.print_fuel.as_deref() {
-            eprintln!("Fuel used by {}: {}", fuel, sess.print_fuel.load(Ordering::SeqCst));
-        }
-
         Ok(())
     })
 }
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 16e53a27128..12dec75e65c 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1,7 +1,7 @@
 use std::fmt;
 
 use rustc_abi::ExternAbi;
-use rustc_ast::util::parser::ExprPrecedence;
+use rustc_ast::util::parser::{AssocOp, PREC_CLOSURE, PREC_JUMP, PREC_PREFIX, PREC_UNAMBIGUOUS};
 use rustc_ast::{
     self as ast, Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label,
     LitKind, TraitObjectSyntax, UintTy,
@@ -261,8 +261,6 @@ pub struct ConstArg<'hir> {
     #[stable_hasher(ignore)]
     pub hir_id: HirId,
     pub kind: ConstArgKind<'hir>,
-    /// Indicates whether this comes from a `~const` desugaring.
-    pub is_desugared_from_effects: bool,
 }
 
 impl<'hir> ConstArg<'hir> {
@@ -462,14 +460,7 @@ impl<'hir> GenericArgs<'hir> {
     /// This function returns the number of type and const generic params.
     /// It should only be used for diagnostics.
     pub fn num_generic_params(&self) -> usize {
-        self.args
-            .iter()
-            .filter(|arg| match arg {
-                GenericArg::Lifetime(_)
-                | GenericArg::Const(ConstArg { is_desugared_from_effects: true, .. }) => false,
-                _ => true,
-            })
-            .count()
+        self.args.iter().filter(|arg| !matches!(arg, GenericArg::Lifetime(_))).count()
     }
 
     /// The span encompassing the arguments and constraints[^1] inside the surrounding brackets.
@@ -690,8 +681,8 @@ impl<'hir> Generics<'hir> {
         if self.has_where_clause_predicates {
             self.predicates
                 .iter()
-                .rfind(|&p| p.in_where_clause())
-                .map_or(end, |p| p.span())
+                .rfind(|&p| p.kind.in_where_clause())
+                .map_or(end, |p| p.span)
                 .shrink_to_hi()
                 .to(end)
         } else {
@@ -714,8 +705,10 @@ impl<'hir> Generics<'hir> {
         &self,
         param_def_id: LocalDefId,
     ) -> impl Iterator<Item = &WhereBoundPredicate<'hir>> {
-        self.predicates.iter().filter_map(move |pred| match pred {
-            WherePredicate::BoundPredicate(bp) if bp.is_param_bound(param_def_id.to_def_id()) => {
+        self.predicates.iter().filter_map(move |pred| match pred.kind {
+            WherePredicateKind::BoundPredicate(bp)
+                if bp.is_param_bound(param_def_id.to_def_id()) =>
+            {
                 Some(bp)
             }
             _ => None,
@@ -726,8 +719,8 @@ impl<'hir> Generics<'hir> {
         &self,
         param_def_id: LocalDefId,
     ) -> impl Iterator<Item = &WhereRegionPredicate<'_>> {
-        self.predicates.iter().filter_map(move |pred| match pred {
-            WherePredicate::RegionPredicate(rp) if rp.is_param_bound(param_def_id) => Some(rp),
+        self.predicates.iter().filter_map(move |pred| match pred.kind {
+            WherePredicateKind::RegionPredicate(rp) if rp.is_param_bound(param_def_id) => Some(rp),
             _ => None,
         })
     }
@@ -779,9 +772,9 @@ impl<'hir> Generics<'hir> {
 
     pub fn span_for_predicate_removal(&self, pos: usize) -> Span {
         let predicate = &self.predicates[pos];
-        let span = predicate.span();
+        let span = predicate.span;
 
-        if !predicate.in_where_clause() {
+        if !predicate.kind.in_where_clause() {
             // <T: ?Sized, U>
             //   ^^^^^^^^
             return span;
@@ -790,19 +783,19 @@ impl<'hir> Generics<'hir> {
         // We need to find out which comma to remove.
         if pos < self.predicates.len() - 1 {
             let next_pred = &self.predicates[pos + 1];
-            if next_pred.in_where_clause() {
+            if next_pred.kind.in_where_clause() {
                 // where T: ?Sized, Foo: Bar,
                 //       ^^^^^^^^^^^
-                return span.until(next_pred.span());
+                return span.until(next_pred.span);
             }
         }
 
         if pos > 0 {
             let prev_pred = &self.predicates[pos - 1];
-            if prev_pred.in_where_clause() {
+            if prev_pred.kind.in_where_clause() {
                 // where Foo: Bar, T: ?Sized,
                 //               ^^^^^^^^^^^
-                return prev_pred.span().shrink_to_hi().to(span);
+                return prev_pred.span.shrink_to_hi().to(span);
             }
         }
 
@@ -814,7 +807,7 @@ impl<'hir> Generics<'hir> {
 
     pub fn span_for_bound_removal(&self, predicate_pos: usize, bound_pos: usize) -> Span {
         let predicate = &self.predicates[predicate_pos];
-        let bounds = predicate.bounds();
+        let bounds = predicate.kind.bounds();
 
         if bounds.len() == 1 {
             return self.span_for_predicate_removal(predicate_pos);
@@ -841,7 +834,15 @@ impl<'hir> Generics<'hir> {
 
 /// A single predicate in a where-clause.
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
-pub enum WherePredicate<'hir> {
+pub struct WherePredicate<'hir> {
+    pub hir_id: HirId,
+    pub span: Span,
+    pub kind: &'hir WherePredicateKind<'hir>,
+}
+
+/// The kind of a single predicate in a where-clause.
+#[derive(Debug, Clone, Copy, HashStable_Generic)]
+pub enum WherePredicateKind<'hir> {
     /// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`).
     BoundPredicate(WhereBoundPredicate<'hir>),
     /// A lifetime predicate (e.g., `'a: 'b + 'c`).
@@ -850,28 +851,20 @@ pub enum WherePredicate<'hir> {
     EqPredicate(WhereEqPredicate<'hir>),
 }
 
-impl<'hir> WherePredicate<'hir> {
-    pub fn span(&self) -> Span {
-        match self {
-            WherePredicate::BoundPredicate(p) => p.span,
-            WherePredicate::RegionPredicate(p) => p.span,
-            WherePredicate::EqPredicate(p) => p.span,
-        }
-    }
-
+impl<'hir> WherePredicateKind<'hir> {
     pub fn in_where_clause(&self) -> bool {
         match self {
-            WherePredicate::BoundPredicate(p) => p.origin == PredicateOrigin::WhereClause,
-            WherePredicate::RegionPredicate(p) => p.in_where_clause,
-            WherePredicate::EqPredicate(_) => false,
+            WherePredicateKind::BoundPredicate(p) => p.origin == PredicateOrigin::WhereClause,
+            WherePredicateKind::RegionPredicate(p) => p.in_where_clause,
+            WherePredicateKind::EqPredicate(_) => false,
         }
     }
 
     pub fn bounds(&self) -> GenericBounds<'hir> {
         match self {
-            WherePredicate::BoundPredicate(p) => p.bounds,
-            WherePredicate::RegionPredicate(p) => p.bounds,
-            WherePredicate::EqPredicate(_) => &[],
+            WherePredicateKind::BoundPredicate(p) => p.bounds,
+            WherePredicateKind::RegionPredicate(p) => p.bounds,
+            WherePredicateKind::EqPredicate(_) => &[],
         }
     }
 }
@@ -886,8 +879,6 @@ pub enum PredicateOrigin {
 /// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`).
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub struct WhereBoundPredicate<'hir> {
-    pub hir_id: HirId,
-    pub span: Span,
     /// Origin of the predicate.
     pub origin: PredicateOrigin,
     /// Any generics from a `for` binding.
@@ -908,7 +899,6 @@ impl<'hir> WhereBoundPredicate<'hir> {
 /// A lifetime predicate (e.g., `'a: 'b + 'c`).
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub struct WhereRegionPredicate<'hir> {
-    pub span: Span,
     pub in_where_clause: bool,
     pub lifetime: &'hir Lifetime,
     pub bounds: GenericBounds<'hir>,
@@ -924,7 +914,6 @@ impl<'hir> WhereRegionPredicate<'hir> {
 /// An equality predicate (e.g., `T = int`); currently unsupported.
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub struct WhereEqPredicate<'hir> {
-    pub span: Span,
     pub lhs_ty: &'hir Ty<'hir>,
     pub rhs_ty: &'hir Ty<'hir>,
 }
@@ -1719,41 +1708,54 @@ pub struct Expr<'hir> {
 }
 
 impl Expr<'_> {
-    pub fn precedence(&self) -> ExprPrecedence {
+    pub fn precedence(&self) -> i8 {
         match self.kind {
-            ExprKind::ConstBlock(_) => ExprPrecedence::ConstBlock,
-            ExprKind::Array(_) => ExprPrecedence::Array,
-            ExprKind::Call(..) => ExprPrecedence::Call,
-            ExprKind::MethodCall(..) => ExprPrecedence::MethodCall,
-            ExprKind::Tup(_) => ExprPrecedence::Tup,
-            ExprKind::Binary(op, ..) => ExprPrecedence::Binary(op.node),
-            ExprKind::Unary(..) => ExprPrecedence::Unary,
-            ExprKind::Lit(_) => ExprPrecedence::Lit,
-            ExprKind::Cast(..) => ExprPrecedence::Cast,
+            ExprKind::Closure { .. } => PREC_CLOSURE,
+
+            ExprKind::Break(..)
+            | ExprKind::Continue(..)
+            | ExprKind::Ret(..)
+            | ExprKind::Yield(..)
+            | ExprKind::Become(..) => PREC_JUMP,
+
+            // Binop-like expr kinds, handled by `AssocOp`.
+            ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence() as i8,
+            ExprKind::Cast(..) => AssocOp::As.precedence() as i8,
+
+            ExprKind::Assign(..) |
+            ExprKind::AssignOp(..) => AssocOp::Assign.precedence() as i8,
+
+            // Unary, prefix
+            ExprKind::AddrOf(..)
+            // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`.
+            // However, this is not exactly right. When `let _ = a` is the LHS of a binop we
+            // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b`
+            // but we need to print `(let _ = a) < b` as-is with parens.
+            | ExprKind::Let(..)
+            | ExprKind::Unary(..) => PREC_PREFIX,
+
+            // Never need parens
+            ExprKind::Array(_)
+            | ExprKind::Block(..)
+            | ExprKind::Call(..)
+            | ExprKind::ConstBlock(_)
+            | ExprKind::Field(..)
+            | ExprKind::If(..)
+            | ExprKind::Index(..)
+            | ExprKind::InlineAsm(..)
+            | ExprKind::Lit(_)
+            | ExprKind::Loop(..)
+            | ExprKind::Match(..)
+            | ExprKind::MethodCall(..)
+            | ExprKind::OffsetOf(..)
+            | ExprKind::Path(..)
+            | ExprKind::Repeat(..)
+            | ExprKind::Struct(..)
+            | ExprKind::Tup(_)
+            | ExprKind::Type(..)
+            | ExprKind::Err(_) => PREC_UNAMBIGUOUS,
+
             ExprKind::DropTemps(ref expr, ..) => expr.precedence(),
-            ExprKind::If(..) => ExprPrecedence::If,
-            ExprKind::Let(..) => ExprPrecedence::Let,
-            ExprKind::Loop(..) => ExprPrecedence::Loop,
-            ExprKind::Match(..) => ExprPrecedence::Match,
-            ExprKind::Closure { .. } => ExprPrecedence::Closure,
-            ExprKind::Block(..) => ExprPrecedence::Block,
-            ExprKind::Assign(..) => ExprPrecedence::Assign,
-            ExprKind::AssignOp(..) => ExprPrecedence::AssignOp,
-            ExprKind::Field(..) => ExprPrecedence::Field,
-            ExprKind::Index(..) => ExprPrecedence::Index,
-            ExprKind::Path(..) => ExprPrecedence::Path,
-            ExprKind::AddrOf(..) => ExprPrecedence::AddrOf,
-            ExprKind::Break(..) => ExprPrecedence::Break,
-            ExprKind::Continue(..) => ExprPrecedence::Continue,
-            ExprKind::Ret(..) => ExprPrecedence::Ret,
-            ExprKind::Become(..) => ExprPrecedence::Become,
-            ExprKind::Struct(..) => ExprPrecedence::Struct,
-            ExprKind::Repeat(..) => ExprPrecedence::Repeat,
-            ExprKind::Yield(..) => ExprPrecedence::Yield,
-            ExprKind::Type(..) | ExprKind::InlineAsm(..) | ExprKind::OffsetOf(..) => {
-                ExprPrecedence::Mac
-            }
-            ExprKind::Err(_) => ExprPrecedence::Err,
         }
     }
 
@@ -3798,7 +3800,7 @@ pub enum Node<'hir> {
     GenericParam(&'hir GenericParam<'hir>),
     Crate(&'hir Mod<'hir>),
     Infer(&'hir InferArg),
-    WhereBoundPredicate(&'hir WhereBoundPredicate<'hir>),
+    WherePredicate(&'hir WherePredicate<'hir>),
     // FIXME: Merge into `Node::Infer`.
     ArrayLenInfer(&'hir InferArg),
     PreciseCapturingNonLifetimeArg(&'hir PreciseCapturingNonLifetimeArg),
@@ -3853,7 +3855,7 @@ impl<'hir> Node<'hir> {
             | Node::TraitRef(..)
             | Node::OpaqueTy(..)
             | Node::Infer(..)
-            | Node::WhereBoundPredicate(..)
+            | Node::WherePredicate(..)
             | Node::ArrayLenInfer(..)
             | Node::Synthetic
             | Node::Err(..) => None,
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index a453af3f7fd..faaea41047f 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -961,30 +961,28 @@ pub fn walk_where_predicate<'v, V: Visitor<'v>>(
     visitor: &mut V,
     predicate: &'v WherePredicate<'v>,
 ) -> V::Result {
-    match *predicate {
-        WherePredicate::BoundPredicate(WhereBoundPredicate {
-            hir_id,
+    let &WherePredicate { hir_id, kind, span: _ } = predicate;
+    try_visit!(visitor.visit_id(hir_id));
+    match *kind {
+        WherePredicateKind::BoundPredicate(WhereBoundPredicate {
             ref bounded_ty,
             bounds,
             bound_generic_params,
             origin: _,
-            span: _,
         }) => {
-            try_visit!(visitor.visit_id(hir_id));
             try_visit!(visitor.visit_ty(bounded_ty));
             walk_list!(visitor, visit_param_bound, bounds);
             walk_list!(visitor, visit_generic_param, bound_generic_params);
         }
-        WherePredicate::RegionPredicate(WhereRegionPredicate {
+        WherePredicateKind::RegionPredicate(WhereRegionPredicate {
             ref lifetime,
             bounds,
-            span: _,
             in_where_clause: _,
         }) => {
             try_visit!(visitor.visit_lifetime(lifetime));
             walk_list!(visitor, visit_param_bound, bounds);
         }
-        WherePredicate::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty, span: _ }) => {
+        WherePredicateKind::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty }) => {
             try_visit!(visitor.visit_ty(lhs_ty));
             try_visit!(visitor.visit_ty(rhs_ty));
         }
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 f86ca95a954..2891b48a154 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -1100,7 +1100,7 @@ fn check_region_bounds_on_impl_item<'tcx>(
             // FIXME: we could potentially look at the impl's bounds to not point at bounds that
             // *are* present in the impl.
             for p in trait_generics.predicates {
-                if let hir::WherePredicate::BoundPredicate(pred) = p {
+                if let hir::WherePredicateKind::BoundPredicate(pred) = p.kind {
                     for b in pred.bounds {
                         if let hir::GenericBound::Outlives(lt) = b {
                             bounds_span.push(lt.ident.span);
@@ -1113,7 +1113,7 @@ fn check_region_bounds_on_impl_item<'tcx>(
             {
                 let mut impl_bounds = 0;
                 for p in impl_generics.predicates {
-                    if let hir::WherePredicate::BoundPredicate(pred) = p {
+                    if let hir::WherePredicateKind::BoundPredicate(pred) = p.kind {
                         for b in pred.bounds {
                             if let hir::GenericBound::Outlives(_) = b {
                                 impl_bounds += 1;
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 9a70555fede..8fa797db246 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -1921,8 +1921,8 @@ fn check_variances_for_type_defn<'tcx>(
         hir_generics
             .predicates
             .iter()
-            .filter_map(|predicate| match predicate {
-                hir::WherePredicate::BoundPredicate(predicate) => {
+            .filter_map(|predicate| match predicate.kind {
+                hir::WherePredicateKind::BoundPredicate(predicate) => {
                     match icx.lower_ty(predicate.bounded_ty).kind() {
                         ty::Param(data) => Some(Parameter(data.index)),
                         _ => None,
@@ -2202,8 +2202,8 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
                     span = predicates
                         .iter()
                         // There seems to be no better way to find out which predicate we are in
-                        .find(|pred| pred.span().contains(obligation_span))
-                        .map(|pred| pred.span())
+                        .find(|pred| pred.span.contains(obligation_span))
+                        .map(|pred| pred.span)
                         .unwrap_or(obligation_span);
                 }
 
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index b5dee5bd021..bfee5d33598 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -238,11 +238,11 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
     trace!(?predicates);
     // Add inline `<T: Foo>` bounds and bounds in the where clause.
     for predicate in hir_generics.predicates {
-        match predicate {
-            hir::WherePredicate::BoundPredicate(bound_pred) => {
+        match predicate.kind {
+            hir::WherePredicateKind::BoundPredicate(bound_pred) => {
                 let ty = icx.lowerer().lower_ty_maybe_return_type_notation(bound_pred.bounded_ty);
 
-                let bound_vars = tcx.late_bound_vars(bound_pred.hir_id);
+                let bound_vars = tcx.late_bound_vars(predicate.hir_id);
                 // Keep the type around in a dummy predicate, in case of no bounds.
                 // That way, `where Ty:` is not a complete noop (see #53696) and `Ty`
                 // is still checked for WF.
@@ -275,7 +275,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
                 predicates.extend(bounds.clauses());
             }
 
-            hir::WherePredicate::RegionPredicate(region_pred) => {
+            hir::WherePredicateKind::RegionPredicate(region_pred) => {
                 let r1 = icx
                     .lowerer()
                     .lower_lifetime(region_pred.lifetime, RegionInferReason::RegionPredicate);
@@ -298,7 +298,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
                 }))
             }
 
-            hir::WherePredicate::EqPredicate(..) => {
+            hir::WherePredicateKind::EqPredicate(..) => {
                 // FIXME(#20041)
             }
         }
@@ -881,7 +881,8 @@ impl<'tcx> ItemCtxt<'tcx> {
         let mut bounds = Bounds::default();
 
         for predicate in hir_generics.predicates {
-            let hir::WherePredicate::BoundPredicate(predicate) = predicate else {
+            let hir_id = predicate.hir_id;
+            let hir::WherePredicateKind::BoundPredicate(predicate) = predicate.kind else {
                 continue;
             };
 
@@ -901,7 +902,7 @@ impl<'tcx> ItemCtxt<'tcx> {
 
             let bound_ty = self.lowerer().lower_ty_maybe_return_type_notation(predicate.bounded_ty);
 
-            let bound_vars = self.tcx.late_bound_vars(predicate.hir_id);
+            let bound_vars = self.tcx.late_bound_vars(hir_id);
             self.lowerer().lower_bounds(
                 bound_ty,
                 predicate.bounds,
@@ -974,10 +975,10 @@ pub(super) fn const_conditions<'tcx>(
     let mut bounds = Bounds::default();
 
     for pred in generics.predicates {
-        match pred {
-            hir::WherePredicate::BoundPredicate(bound_pred) => {
+        match pred.kind {
+            hir::WherePredicateKind::BoundPredicate(bound_pred) => {
                 let ty = icx.lowerer().lower_ty_maybe_return_type_notation(bound_pred.bounded_ty);
-                let bound_vars = tcx.late_bound_vars(bound_pred.hir_id);
+                let bound_vars = tcx.late_bound_vars(pred.hir_id);
                 icx.lowerer().lower_bounds(
                     ty,
                     bound_pred.bounds.iter(),
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 b4b3ef31f97..74729ebe488 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -936,9 +936,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
     }
 
     fn visit_where_predicate(&mut self, predicate: &'tcx hir::WherePredicate<'tcx>) {
-        match predicate {
-            &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
-                hir_id,
+        let hir_id = predicate.hir_id;
+        match predicate.kind {
+            &hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate {
                 bounded_ty,
                 bounds,
                 bound_generic_params,
@@ -979,7 +979,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                     walk_list!(this, visit_param_bound, bounds);
                 })
             }
-            &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
+            &hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate {
                 lifetime,
                 bounds,
                 ..
@@ -987,7 +987,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                 self.visit_lifetime(lifetime);
                 walk_list!(self, visit_param_bound, bounds);
             }
-            &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => {
+            &hir::WherePredicateKind::EqPredicate(hir::WhereEqPredicate {
+                lhs_ty, rhs_ty, ..
+            }) => {
                 self.visit_ty(lhs_ty);
                 self.visit_ty(rhs_ty);
             }
@@ -2073,7 +2075,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                         // bounds since we'll be emitting a hard error in HIR lowering, so this
                         // is purely speculative.
                         let one_bound = generics.predicates.iter().find_map(|predicate| {
-                            let hir::WherePredicate::BoundPredicate(predicate) = predicate else {
+                            let hir::WherePredicateKind::BoundPredicate(predicate) = predicate.kind
+                            else {
                                 return None;
                             };
                             let hir::TyKind::Path(hir::QPath::Resolved(None, bounded_path)) =
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 6ebe1cedcaf..9d60759ae48 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -69,7 +69,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         search_bounds(hir_bounds);
         if let Some((self_ty, where_clause)) = self_ty_where_predicates {
             for clause in where_clause {
-                if let hir::WherePredicate::BoundPredicate(pred) = clause
+                if let hir::WherePredicateKind::BoundPredicate(pred) = clause.kind
                     && pred.is_param_bound(self_ty.to_def_id())
                 {
                     search_bounds(pred.bounds);
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 5e27ace4cbe..f6e227377b9 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
@@ -8,7 +8,8 @@ use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS;
 use rustc_middle::span_bug;
 use rustc_middle::ty::fold::BottomUpFolder;
 use rustc_middle::ty::{
-    self, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, Upcast,
+    self, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable,
+    TypeVisitableExt, Upcast,
 };
 use rustc_span::{ErrorGuaranteed, Span};
 use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility;
@@ -92,11 +93,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
         let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) =
             expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id()));
+
+        // We don't support >1 principal
         if regular_traits.len() > 1 {
-            let _ = self.report_trait_object_addition_traits_error(&regular_traits);
-        } else if regular_traits.is_empty() && auto_traits.is_empty() {
-            let reported = self.report_trait_object_with_no_traits_error(span, &trait_bounds);
-            return Ty::new_error(tcx, reported);
+            let guar = self.report_trait_object_addition_traits_error(&regular_traits);
+            return Ty::new_error(tcx, guar);
+        }
+        // We  don't support empty trait objects.
+        if regular_traits.is_empty() && auto_traits.is_empty() {
+            let guar = self.report_trait_object_with_no_traits_error(span, &trait_bounds);
+            return Ty::new_error(tcx, guar);
+        }
+        // Don't create a dyn trait if we have errors in the principal.
+        if let Err(guar) = trait_bounds.error_reported() {
+            return Ty::new_error(tcx, guar);
         }
 
         // Check that there are no gross dyn-compatibility violations;
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 0a3aa8fec90..64c6969d4b9 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -117,11 +117,7 @@ impl<'a> State<'a> {
             Node::Ctor(..) => panic!("cannot print isolated Ctor"),
             Node::LetStmt(a) => self.print_local_decl(a),
             Node::Crate(..) => panic!("cannot print Crate"),
-            Node::WhereBoundPredicate(pred) => {
-                self.print_formal_generic_params(pred.bound_generic_params);
-                self.print_type(pred.bounded_ty);
-                self.print_bounds(":", pred.bounds);
-            }
+            Node::WherePredicate(pred) => self.print_where_predicate(pred),
             Node::ArrayLenInfer(_) => self.word("_"),
             Node::Synthetic => unreachable!(),
             Node::Err(_) => self.word("/*ERROR*/"),
@@ -1015,7 +1011,7 @@ impl<'a> State<'a> {
     }
 
     fn print_expr_maybe_paren(&mut self, expr: &hir::Expr<'_>, prec: i8) {
-        self.print_expr_cond_paren(expr, expr.precedence().order() < prec)
+        self.print_expr_cond_paren(expr, expr.precedence() < prec)
     }
 
     /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in
@@ -1049,7 +1045,7 @@ impl<'a> State<'a> {
         }
         self.space();
         self.word_space("=");
-        let npals = || parser::needs_par_as_let_scrutinee(init.precedence().order());
+        let npals = || parser::needs_par_as_let_scrutinee(init.precedence());
         self.print_expr_cond_paren(init, Self::cond_needs_par(init) || npals())
     }
 
@@ -2160,47 +2156,50 @@ impl<'a> State<'a> {
             if i != 0 {
                 self.word_space(",");
             }
+            self.print_where_predicate(predicate);
+        }
+    }
 
-            match *predicate {
-                hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
-                    bound_generic_params,
-                    bounded_ty,
-                    bounds,
-                    ..
-                }) => {
-                    self.print_formal_generic_params(bound_generic_params);
-                    self.print_type(bounded_ty);
-                    self.print_bounds(":", bounds);
-                }
-                hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
-                    lifetime,
-                    bounds,
-                    ..
-                }) => {
-                    self.print_lifetime(lifetime);
-                    self.word(":");
-
-                    for (i, bound) in bounds.iter().enumerate() {
-                        match bound {
-                            GenericBound::Outlives(lt) => {
-                                self.print_lifetime(lt);
-                            }
-                            _ => panic!("unexpected bound on lifetime param: {bound:?}"),
-                        }
+    fn print_where_predicate(&mut self, predicate: &hir::WherePredicate<'_>) {
+        match *predicate.kind {
+            hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate {
+                bound_generic_params,
+                bounded_ty,
+                bounds,
+                ..
+            }) => {
+                self.print_formal_generic_params(bound_generic_params);
+                self.print_type(bounded_ty);
+                self.print_bounds(":", bounds);
+            }
+            hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate {
+                lifetime,
+                bounds,
+                ..
+            }) => {
+                self.print_lifetime(lifetime);
+                self.word(":");
 
-                        if i != 0 {
-                            self.word(":");
+                for (i, bound) in bounds.iter().enumerate() {
+                    match bound {
+                        GenericBound::Outlives(lt) => {
+                            self.print_lifetime(lt);
                         }
+                        _ => panic!("unexpected bound on lifetime param: {bound:?}"),
+                    }
+
+                    if i != 0 {
+                        self.word(":");
                     }
                 }
-                hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
-                    lhs_ty, rhs_ty, ..
-                }) => {
-                    self.print_type(lhs_ty);
-                    self.space();
-                    self.word_space("=");
-                    self.print_type(rhs_ty);
-                }
+            }
+            hir::WherePredicateKind::EqPredicate(hir::WhereEqPredicate {
+                lhs_ty, rhs_ty, ..
+            }) => {
+                self.print_type(lhs_ty);
+                self.space();
+                self.word_space("=");
+                self.print_type(rhs_ty);
             }
         }
     }
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index e6e0f62b54d..a92482e6a0e 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -606,7 +606,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             };
 
             if let Ok(rest_snippet) = rest_snippet {
-                let sugg = if callee_expr.precedence().order() >= PREC_UNAMBIGUOUS {
+                let sugg = if callee_expr.precedence() >= PREC_UNAMBIGUOUS {
                     vec![
                         (up_to_rcvr_span, "".to_string()),
                         (rest_span, format!(".{}({rest_snippet}", segment.ident)),
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 483a8d1d9a9..0c3f21d540d 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -1107,7 +1107,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
     }
 
     fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) {
-        let expr_prec = self.expr.precedence().order();
+        let expr_prec = self.expr.precedence();
         let needs_parens = expr_prec < rustc_ast::util::parser::PREC_UNAMBIGUOUS;
 
         let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize));
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 1610848958e..6599b49baa3 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -420,7 +420,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             | hir::Node::GenericParam(_)
             | hir::Node::Crate(_)
             | hir::Node::Infer(_)
-            | hir::Node::WhereBoundPredicate(_)
+            | hir::Node::WherePredicate(_)
             | hir::Node::ArrayLenInfer(_)
             | hir::Node::PreciseCapturingNonLifetimeArg(_)
             | hir::Node::OpaqueTy(_) => {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 9c18dbd422d..f8f6564cf14 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -2347,9 +2347,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 let check_for_matched_generics = || {
                     if matched_inputs.iter().any(|x| x.is_some())
-                        && params_with_generics.iter().any(|x| x.0.is_some())
+                        && params_with_generics.iter().any(|x| x.1.is_some())
                     {
-                        for (idx, (generic, _)) in params_with_generics.iter().enumerate() {
+                        for &(idx, generic, _) in &params_with_generics {
                             // Param has to have a generic and be matched to be relevant
                             if matched_inputs[idx.into()].is_none() {
                                 continue;
@@ -2362,7 +2362,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             for unmatching_idx in idx + 1..params_with_generics.len() {
                                 if matched_inputs[unmatching_idx.into()].is_none()
                                     && let Some(unmatched_idx_param_generic) =
-                                        params_with_generics[unmatching_idx].0
+                                        params_with_generics[unmatching_idx].1
                                     && unmatched_idx_param_generic.name.ident()
                                         == generic.name.ident()
                                 {
@@ -2377,8 +2377,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 let check_for_matched_generics = check_for_matched_generics();
 
-                for (idx, (generic_param, param)) in
-                    params_with_generics.iter().enumerate().filter(|(idx, _)| {
+                for &(idx, generic_param, param) in
+                    params_with_generics.iter().filter(|&(idx, _, _)| {
                         check_for_matched_generics
                             || expected_idx.is_none_or(|expected_idx| expected_idx == *idx)
                     })
@@ -2390,8 +2390,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                     let other_params_matched: Vec<(usize, &hir::Param<'_>)> = params_with_generics
                         .iter()
-                        .enumerate()
-                        .filter(|(other_idx, (other_generic_param, _))| {
+                        .filter(|(other_idx, other_generic_param, _)| {
                             if *other_idx == idx {
                                 return false;
                             }
@@ -2410,18 +2409,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             }
                             other_generic_param.name.ident() == generic_param.name.ident()
                         })
-                        .map(|(other_idx, (_, other_param))| (other_idx, *other_param))
+                        .map(|&(other_idx, _, other_param)| (other_idx, other_param))
                         .collect();
 
                     if !other_params_matched.is_empty() {
                         let other_param_matched_names: Vec<String> = other_params_matched
                             .iter()
-                            .map(|(_, other_param)| {
+                            .map(|(idx, other_param)| {
                                 if let hir::PatKind::Binding(_, _, ident, _) = other_param.pat.kind
                                 {
                                     format!("`{ident}`")
                                 } else {
-                                    "{unknown}".to_string()
+                                    format!("parameter #{}", idx + 1)
                                 }
                             })
                             .collect();
@@ -2478,18 +2477,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 {
                     let param_idents_matching: Vec<String> = params_with_generics
                         .iter()
-                        .filter(|(generic, _)| {
+                        .filter(|(_, generic, _)| {
                             if let Some(generic) = generic {
                                 generic.name.ident() == generic_param.name.ident()
                             } else {
                                 false
                             }
                         })
-                        .map(|(_, param)| {
+                        .map(|(idx, _, param)| {
                             if let hir::PatKind::Binding(_, _, ident, _) = param.pat.kind {
                                 format!("`{ident}`")
                             } else {
-                                "{unknown}".to_string()
+                                format!("parameter #{}", idx + 1)
                             }
                         })
                         .collect();
@@ -2498,8 +2497,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         spans.push_span_label(
                             generic_param.span,
                             format!(
-                                "{} all reference this parameter {}",
+                                "{} {} reference this parameter `{}`",
                                 display_list_with_comma_and(&param_idents_matching),
+                                if param_idents_matching.len() == 2 { "both" } else { "all" },
                                 generic_param.name.ident().name,
                             ),
                         );
@@ -2580,7 +2580,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         if let Some(params_with_generics) = self.get_hir_params_with_generics(def_id, is_method) {
             debug_assert_eq!(params_with_generics.len(), matched_inputs.len());
-            for (idx, (generic_param, _)) in params_with_generics.iter().enumerate() {
+            for &(idx, generic_param, _) in &params_with_generics {
                 if matched_inputs[idx.into()].is_none() {
                     continue;
                 }
@@ -2594,20 +2594,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 };
 
                 let mut idxs_matched: Vec<usize> = vec![];
-                for (other_idx, (_, _)) in params_with_generics.iter().enumerate().filter(
-                    |(other_idx, (other_generic_param, _))| {
-                        if *other_idx == idx {
+                for &(other_idx, _, _) in
+                    params_with_generics.iter().filter(|&&(other_idx, other_generic_param, _)| {
+                        if other_idx == idx {
                             return false;
                         }
                         let Some(other_generic_param) = other_generic_param else {
                             return false;
                         };
-                        if matched_inputs[(*other_idx).into()].is_some() {
+                        if matched_inputs[other_idx.into()].is_some() {
                             return false;
                         }
                         other_generic_param.name.ident() == generic_param.name.ident()
-                    },
-                ) {
+                    })
+                {
                     idxs_matched.push(other_idx);
                 }
 
@@ -2642,7 +2642,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         def_id: DefId,
         is_method: bool,
-    ) -> Option<Vec<(Option<&hir::GenericParam<'_>>, &hir::Param<'_>)>> {
+    ) -> Option<Vec<(usize, Option<&hir::GenericParam<'_>>, &hir::Param<'_>)>> {
         let fn_node = self.tcx.hir().get_if_local(def_id)?;
         let fn_decl = fn_node.fn_decl()?;
 
@@ -2685,7 +2685,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         debug_assert_eq!(params.len(), generic_params.len());
-        Some(generic_params.into_iter().zip(params).collect())
+        Some(
+            generic_params
+                .into_iter()
+                .zip(params)
+                .enumerate()
+                .map(|(a, (b, c))| (a, b, c))
+                .collect(),
+        )
     }
 }
 
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index c4c4c2f200b..b493a61b9f4 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -2,7 +2,7 @@ use core::cmp::min;
 use core::iter;
 
 use hir::def_id::LocalDefId;
-use rustc_ast::util::parser::{ExprPrecedence, PREC_UNAMBIGUOUS};
+use rustc_ast::util::parser::PREC_UNAMBIGUOUS;
 use rustc_data_structures::packed::Pu128;
 use rustc_errors::{Applicability, Diag, MultiSpan};
 use rustc_hir as hir;
@@ -10,7 +10,7 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{
     Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId,
-    Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
+    Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicateKind,
 };
 use rustc_hir_analysis::collect::suggest_impl_trait;
 use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
@@ -398,7 +398,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // so we remove the user's `clone` call.
                     {
                         vec![(receiver_method.ident.span, conversion_method.name.to_string())]
-                    } else if expr.precedence().order() < ExprPrecedence::MethodCall.order() {
+                    } else if expr.precedence() < PREC_UNAMBIGUOUS {
                         vec![
                             (expr.span.shrink_to_lo(), "(".to_string()),
                             (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)),
@@ -1004,8 +1004,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // get all where BoundPredicates here, because they are used in two cases below
         let where_predicates = predicates
             .iter()
-            .filter_map(|p| match p {
-                WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
+            .filter_map(|p| match p.kind {
+                WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate {
                     bounds,
                     bounded_ty,
                     ..
@@ -1376,7 +1376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         {
             let span = expr.span.find_oldest_ancestor_in_same_ctxt();
 
-            let mut sugg = if expr.precedence().order() >= PREC_UNAMBIGUOUS {
+            let mut sugg = if expr.precedence() >= PREC_UNAMBIGUOUS {
                 vec![(span.shrink_to_hi(), ".into()".to_owned())]
             } else {
                 vec![
@@ -3000,7 +3000,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             "change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`",
         );
 
-        let close_paren = if expr.precedence().order() < PREC_UNAMBIGUOUS {
+        let close_paren = if expr.precedence() < PREC_UNAMBIGUOUS {
             sugg.push((expr.span.shrink_to_lo(), "(".to_string()));
             ")"
         } else {
@@ -3025,7 +3025,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let len = src.trim_end_matches(&checked_ty.to_string()).len();
                 expr.span.with_lo(expr.span.lo() + BytePos(len as u32))
             },
-            if expr.precedence().order() < PREC_UNAMBIGUOUS {
+            if expr.precedence() < PREC_UNAMBIGUOUS {
                 // Readd `)`
                 format!("{expected_ty})")
             } else {
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index e48c4d46b59..6beae14100d 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -784,7 +784,6 @@ fn test_unstable_options_tracking_hash() {
     tracked!(flatten_format_args, false);
     tracked!(fmt_debug, FmtDebug::Shallow);
     tracked!(force_unstable_if_unmarked, true);
-    tracked!(fuel, Some(("abc".to_string(), 99)));
     tracked!(function_return, FunctionReturn::ThunkExtern);
     tracked!(function_sections, Some(false));
     tracked!(human_readable_cgu_names, true);
@@ -830,7 +829,6 @@ fn test_unstable_options_tracking_hash() {
     tracked!(plt, Some(true));
     tracked!(polonius, Polonius::Legacy);
     tracked!(precise_enum_drop_elaboration, false);
-    tracked!(print_fuel, Some("abc".to_string()));
     tracked!(profile_sample_use, Some(PathBuf::from("abc")));
     tracked!(profiler_runtime, "abc".to_string());
     tracked!(regparm, Some(3));
diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs
index bcb103957ba..b584e7afd98 100644
--- a/compiler/rustc_lexer/src/lib.rs
+++ b/compiler/rustc_lexer/src/lib.rs
@@ -566,19 +566,19 @@ impl Cursor<'_> {
 
     fn c_or_byte_string(
         &mut self,
-        mk_kind: impl FnOnce(bool) -> LiteralKind,
-        mk_kind_raw: impl FnOnce(Option<u8>) -> LiteralKind,
+        mk_kind: fn(bool) -> LiteralKind,
+        mk_kind_raw: fn(Option<u8>) -> LiteralKind,
         single_quoted: Option<fn(bool) -> LiteralKind>,
     ) -> TokenKind {
         match (self.first(), self.second(), single_quoted) {
-            ('\'', _, Some(mk_kind)) => {
+            ('\'', _, Some(single_quoted)) => {
                 self.bump();
                 let terminated = self.single_quoted_string();
                 let suffix_start = self.pos_within_token();
                 if terminated {
                     self.eat_literal_suffix();
                 }
-                let kind = mk_kind(terminated);
+                let kind = single_quoted(terminated);
                 Literal { kind, suffix_start }
             }
             ('"', _, _) => {
diff --git a/compiler/rustc_lexer/src/tests.rs b/compiler/rustc_lexer/src/tests.rs
index 556bbf1f518..db7225fc2a8 100644
--- a/compiler/rustc_lexer/src/tests.rs
+++ b/compiler/rustc_lexer/src/tests.rs
@@ -77,61 +77,51 @@ fn test_too_many_hashes() {
     check_raw_str(&s2, Err(RawStrError::TooManyDelimiters { found: u32::from(max_count) + 1 }));
 }
 
+// https://github.com/rust-lang/rust/issues/70528
 #[test]
 fn test_valid_shebang() {
-    // https://github.com/rust-lang/rust/issues/70528
-    let input = "#!/usr/bin/rustrun\nlet x = 5;";
-    assert_eq!(strip_shebang(input), Some(18));
-}
+    let input = "#!/bin/bash";
+    assert_eq!(strip_shebang(input), Some(input.len()));
 
-#[test]
-fn test_invalid_shebang_valid_rust_syntax() {
-    // https://github.com/rust-lang/rust/issues/70528
-    let input = "#!    [bad_attribute]";
+    let input = "#![attribute]";
     assert_eq!(strip_shebang(input), None);
-}
 
-#[test]
-fn test_shebang_second_line() {
-    // Because shebangs are interpreted by the kernel, they must be on the first line
-    let input = "\n#!/bin/bash";
+    let input = "#!    /bin/bash";
+    assert_eq!(strip_shebang(input), Some(input.len()));
+
+    let input = "#!    [attribute]";
     assert_eq!(strip_shebang(input), None);
-}
 
-#[test]
-fn test_shebang_space() {
-    let input = "#!    /bin/bash";
+    let input = "#! /* blah */  /bin/bash";
     assert_eq!(strip_shebang(input), Some(input.len()));
-}
 
-#[test]
-fn test_shebang_empty_shebang() {
-    let input = "#!    \n[attribute(foo)]";
+    let input = "#! /* blah */  [attribute]";
     assert_eq!(strip_shebang(input), None);
-}
 
-#[test]
-fn test_invalid_shebang_comment() {
-    let input = "#!//bin/ami/a/comment\n[";
-    assert_eq!(strip_shebang(input), None)
-}
+    let input = "#! // blah\n/bin/bash";
+    assert_eq!(strip_shebang(input), Some(10)); // strip up to the newline
 
-#[test]
-fn test_invalid_shebang_another_comment() {
-    let input = "#!/*bin/ami/a/comment*/\n[attribute";
-    assert_eq!(strip_shebang(input), None)
-}
+    let input = "#! // blah\n[attribute]";
+    assert_eq!(strip_shebang(input), None);
 
-#[test]
-fn test_shebang_valid_rust_after() {
-    let input = "#!/*bin/ami/a/comment*/\npub fn main() {}";
-    assert_eq!(strip_shebang(input), Some(23))
-}
+    let input = "#! /* blah\nblah\nblah */  /bin/bash";
+    assert_eq!(strip_shebang(input), Some(10));
 
-#[test]
-fn test_shebang_followed_by_attrib() {
-    let input = "#!/bin/rust-scripts\n#![allow_unused(true)]";
-    assert_eq!(strip_shebang(input), Some(19));
+    let input = "#! /* blah\nblah\nblah */  [attribute]";
+    assert_eq!(strip_shebang(input), None);
+
+    let input = "#!\n/bin/sh";
+    assert_eq!(strip_shebang(input), Some(2));
+
+    let input = "#!\n[attribute]";
+    assert_eq!(strip_shebang(input), None);
+
+    // Because shebangs are interpreted by the kernel, they must be on the first line
+    let input = "\n#!/bin/bash";
+    assert_eq!(strip_shebang(input), None);
+
+    let input = "\n#![attribute]";
+    assert_eq!(strip_shebang(input), None);
 }
 
 fn check_lexing(src: &str, expect: Expect) {
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index bda982a3675..e130cfc1d73 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -1407,7 +1407,7 @@ declare_lint_pass!(TypeAliasBounds => [TYPE_ALIAS_BOUNDS]);
 impl TypeAliasBounds {
     pub(crate) fn affects_object_lifetime_defaults(pred: &hir::WherePredicate<'_>) -> bool {
         // Bounds of the form `T: 'a` with `T` type param affect object lifetime defaults.
-        if let hir::WherePredicate::BoundPredicate(pred) = pred
+        if let hir::WherePredicateKind::BoundPredicate(pred) = pred.kind
             && pred.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Outlives(_)))
             && pred.bound_generic_params.is_empty() // indeed, even if absent from the RHS
             && pred.bounded_ty.as_generic_param().is_some()
@@ -1451,11 +1451,11 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
         let mut inline_sugg = Vec::new();
 
         for p in generics.predicates {
-            let span = p.span();
-            if p.in_where_clause() {
+            let span = p.span;
+            if p.kind.in_where_clause() {
                 where_spans.push(span);
             } else {
-                for b in p.bounds() {
+                for b in p.kind.bounds() {
                     inline_spans.push(b.span());
                 }
                 inline_sugg.push((span, String::new()));
@@ -2071,7 +2071,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
             let num_where_predicates = hir_generics
                 .predicates
                 .iter()
-                .filter(|predicate| predicate.in_where_clause())
+                .filter(|predicate| predicate.kind.in_where_clause())
                 .count();
 
             let mut bound_count = 0;
@@ -2080,8 +2080,8 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
             let mut dropped_where_predicate_count = 0;
             for (i, where_predicate) in hir_generics.predicates.iter().enumerate() {
                 let (relevant_lifetimes, bounds, predicate_span, in_where_clause) =
-                    match where_predicate {
-                        hir::WherePredicate::RegionPredicate(predicate) => {
+                    match where_predicate.kind {
+                        hir::WherePredicateKind::RegionPredicate(predicate) => {
                             if let Some(ResolvedArg::EarlyBound(region_def_id)) =
                                 cx.tcx.named_bound_var(predicate.lifetime.hir_id)
                             {
@@ -2090,21 +2090,21 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
                                         cx.tcx,
                                         // don't warn if the inferred span actually came from the predicate we're looking at
                                         // this happens if the type is recursively defined
-                                        inferred_outlives
-                                            .iter()
-                                            .filter(|(_, span)| !predicate.span.contains(*span)),
+                                        inferred_outlives.iter().filter(|(_, span)| {
+                                            !where_predicate.span.contains(*span)
+                                        }),
                                         item.owner_id.def_id,
                                         region_def_id,
                                     ),
                                     &predicate.bounds,
-                                    predicate.span,
+                                    where_predicate.span,
                                     predicate.in_where_clause,
                                 )
                             } else {
                                 continue;
                             }
                         }
-                        hir::WherePredicate::BoundPredicate(predicate) => {
+                        hir::WherePredicateKind::BoundPredicate(predicate) => {
                             // FIXME we can also infer bounds on associated types,
                             // and should check for them here.
                             match predicate.bounded_ty.kind {
@@ -2118,12 +2118,12 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
                                             // don't warn if the inferred span actually came from the predicate we're looking at
                                             // this happens if the type is recursively defined
                                             inferred_outlives.iter().filter(|(_, span)| {
-                                                !predicate.span.contains(*span)
+                                                !where_predicate.span.contains(*span)
                                             }),
                                             index,
                                         ),
                                         &predicate.bounds,
-                                        predicate.span,
+                                        where_predicate.span,
                                         predicate.origin == PredicateOrigin::WhereClause,
                                     )
                                 }
@@ -2161,7 +2161,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
                     } else if i + 1 < num_where_predicates {
                         // If all the bounds on a predicate were inferable and there are
                         // further predicates, we want to eat the trailing comma.
-                        let next_predicate_span = hir_generics.predicates[i + 1].span();
+                        let next_predicate_span = hir_generics.predicates[i + 1].span;
                         if next_predicate_span.from_expansion() {
                             where_lint_spans.push(predicate_span);
                         } else {
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 352155729e5..dce6010a2c1 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -300,7 +300,7 @@ impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_> {
         let affect_object_lifetime_defaults = self
             .preds
             .iter()
-            .filter(|pred| pred.in_where_clause() == self.in_where_clause)
+            .filter(|pred| pred.kind.in_where_clause() == self.in_where_clause)
             .any(|pred| TypeAliasBounds::affects_object_lifetime_defaults(pred));
 
         // If there are any shorthand assoc tys, then the bounds can't be removed automatically.
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 5ec920d39f4..e1a0e1ec579 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -1304,12 +1304,12 @@ impl EarlyLintPass for UnusedParens {
     }
 
     fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) {
-        use rustc_ast::{WhereBoundPredicate, WherePredicate};
-        if let WherePredicate::BoundPredicate(WhereBoundPredicate {
+        use rustc_ast::{WhereBoundPredicate, WherePredicateKind};
+        if let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
             bounded_ty,
             bound_generic_params,
             ..
-        }) = pred
+        }) = &pred.kind
             && let ast::TyKind::Paren(_) = &bounded_ty.kind
             && bound_generic_params.is_empty()
         {
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index ebae968d005..007d9265165 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -1172,7 +1172,7 @@ fn attempt_load_dylib(path: &Path) -> Result<libloading::Library, libloading::Er
         // the expected format is lib<name>.a(libname.so) for the actual
         // dynamic library
         let library_name = path.file_stem().expect("expect a library name");
-        let mut archive_member = OsString::from("a(");
+        let mut archive_member = std::ffi::OsString::from("a(");
         archive_member.push(library_name);
         archive_member.push(".so)");
         let new_path = path.with_extension(archive_member);
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index 007e6f46006..4900575c36e 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -947,7 +947,7 @@ impl<'hir> Map<'hir> {
             Node::Infer(i) => i.span,
             Node::LetStmt(local) => local.span,
             Node::Crate(item) => item.spans.inner_span,
-            Node::WhereBoundPredicate(pred) => pred.span,
+            Node::WherePredicate(pred) => pred.span,
             Node::ArrayLenInfer(inf) => inf.span,
             Node::PreciseCapturingNonLifetimeArg(param) => param.ident.span,
             Node::Synthetic => unreachable!(),
@@ -1225,7 +1225,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String {
             format!("{id} (generic_param {})", path_str(param.def_id))
         }
         Node::Crate(..) => String::from("(root_crate)"),
-        Node::WhereBoundPredicate(_) => node_str("where bound predicate"),
+        Node::WherePredicate(_) => node_str("where predicate"),
         Node::ArrayLenInfer(_) => node_str("array len infer"),
         Node::Synthetic => unreachable!(),
         Node::Err(_) => node_str("error"),
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index c4d86c3210e..d27205e26ab 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -151,10 +151,6 @@ impl<'tcx> Const<'tcx> {
 }
 
 impl<'tcx> rustc_type_ir::inherent::Const<TyCtxt<'tcx>> for Const<'tcx> {
-    fn try_to_target_usize(self, interner: TyCtxt<'tcx>) -> Option<u64> {
-        self.try_to_target_usize(interner)
-    }
-
     fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferConst) -> Self {
         Const::new_infer(tcx, infer)
     }
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index d982122e2aa..5bf62a17c8e 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -1566,10 +1566,6 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
-    pub fn consider_optimizing<T: Fn() -> String>(self, msg: T) -> bool {
-        self.sess.consider_optimizing(|| self.crate_name(LOCAL_CRATE), msg)
-    }
-
     /// Obtain all lang items of this crate and all dependencies (recursively)
     pub fn lang_items(self) -> &'tcx rustc_hir::lang_items::LanguageItems {
         self.get_lang_items(())
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index 84f52bfe48f..fd807882e0f 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -8,7 +8,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display};
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
-use rustc_hir::{self as hir, LangItem, PredicateOrigin, WherePredicate};
+use rustc_hir::{self as hir, LangItem, PredicateOrigin, WherePredicateKind};
 use rustc_span::{BytePos, Span};
 use rustc_type_ir::TyKind::*;
 
@@ -180,7 +180,7 @@ fn suggest_changing_unsized_bound(
     // First look at the `where` clause because we can have `where T: ?Sized`,
     // then look at params.
     for (where_pos, predicate) in generics.predicates.iter().enumerate() {
-        let WherePredicate::BoundPredicate(predicate) = predicate else {
+        let WherePredicateKind::BoundPredicate(predicate) = predicate.kind else {
             continue;
         };
         if !predicate.is_param_bound(param.def_id.to_def_id()) {
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 43d243b0584..4a82af32559 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -58,12 +58,9 @@ impl<'tcx> TypeError<'tcx> {
                 pluralize!(values.found)
             )
             .into(),
-            TypeError::FixedArraySize(values) => format!(
-                "expected an array with a fixed size of {} element{}, found one with {} element{}",
-                values.expected,
-                pluralize!(values.expected),
-                values.found,
-                pluralize!(values.found)
+            TypeError::ArraySize(values) => format!(
+                "expected an array with a size of {}, found one with a size of {}",
+                values.expected, values.found,
             )
             .into(),
             TypeError::ArgCount => "incorrect number of function parameters".into(),
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 2b532701904..c7a2223ecd7 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1558,10 +1558,7 @@ impl<'tcx> TyCtxt<'tcx> {
         let is_box = self.is_lang_item(did.to_def_id(), LangItem::OwnedBox);
 
         // This is here instead of layout because the choice must make it into metadata.
-        if is_box
-            || !self
-                .consider_optimizing(|| format!("Reorder fields of {:?}", self.def_path_str(did)))
-        {
+        if is_box {
             flags.insert(ReprFlags::IS_LINEAR);
         }
 
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index b06687490d2..afdec7a86d4 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -3,7 +3,6 @@ use std::iter;
 pub use rustc_type_ir::relate::*;
 
 use crate::ty::error::{ExpectedFound, TypeError};
-use crate::ty::predicate::ExistentialPredicateStableCmpExt as _;
 use crate::ty::{self as ty, Ty, TyCtxt};
 
 pub type RelateResult<'tcx, T> = rustc_type_ir::relate::RelateResult<TyCtxt<'tcx>, T>;
@@ -86,10 +85,7 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for &'tcx ty::List<ty::PolyExistentialPredicate<
         // in `a`.
         let mut a_v: Vec<_> = a.into_iter().collect();
         let mut b_v: Vec<_> = b.into_iter().collect();
-        // `skip_binder` here is okay because `stable_cmp` doesn't look at binders
-        a_v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
         a_v.dedup();
-        b_v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
         b_v.dedup();
         if a_v.len() != b_v.len() {
             return Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b)));
diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs
index 5ebb343f4e1..11cf8c3e898 100644
--- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs
@@ -1,6 +1,7 @@
 //! Random access inspection of the results of a dataflow analysis.
 
 use std::cmp::Ordering;
+use std::ops::{Deref, DerefMut};
 
 #[cfg(debug_assertions)]
 use rustc_index::bit_set::BitSet;
@@ -8,18 +9,63 @@ use rustc_middle::mir::{self, BasicBlock, Location};
 
 use super::{Analysis, Direction, Effect, EffectIndex, Results};
 
-/// Allows random access inspection of the results of a dataflow analysis.
+/// Some `ResultsCursor`s want to own a `Results`, and some want to borrow a `Results`, either
+/// mutable or immutably. This type allows all of the above. It's similar to `Cow`.
+pub enum ResultsHandle<'a, 'tcx, A>
+where
+    A: Analysis<'tcx>,
+{
+    Borrowed(&'a Results<'tcx, A>),
+    BorrowedMut(&'a mut Results<'tcx, A>),
+    Owned(Results<'tcx, A>),
+}
+
+impl<'tcx, A> Deref for ResultsHandle<'_, 'tcx, A>
+where
+    A: Analysis<'tcx>,
+{
+    type Target = Results<'tcx, A>;
+
+    fn deref(&self) -> &Results<'tcx, A> {
+        match self {
+            ResultsHandle::Borrowed(borrowed) => borrowed,
+            ResultsHandle::BorrowedMut(borrowed) => borrowed,
+            ResultsHandle::Owned(owned) => owned,
+        }
+    }
+}
+
+impl<'tcx, A> DerefMut for ResultsHandle<'_, 'tcx, A>
+where
+    A: Analysis<'tcx>,
+{
+    fn deref_mut(&mut self) -> &mut Results<'tcx, A> {
+        match self {
+            ResultsHandle::Borrowed(_borrowed) => {
+                panic!("tried to deref_mut a `ResultsHandle::Borrowed")
+            }
+            ResultsHandle::BorrowedMut(borrowed) => borrowed,
+            ResultsHandle::Owned(owned) => owned,
+        }
+    }
+}
+
+/// Allows random access inspection of the results of a dataflow analysis. Use this when you want
+/// to inspect domain values only in certain locations; use `ResultsVisitor` if you want to inspect
+/// domain values in many or all locations.
 ///
-/// This cursor only has linear performance within a basic block when its statements are visited in
-/// the same order as the `DIRECTION` of the analysis. In the worst case—when statements are
-/// visited in *reverse* order—performance will be quadratic in the number of statements in the
-/// block. The order in which basic blocks are inspected has no impact on performance.
+/// Because `Results` only has domain values for the entry of each basic block, these inspections
+/// involve some amount of domain value recomputations. This cursor only has linear performance
+/// within a basic block when its statements are visited in the same order as the `DIRECTION` of
+/// the analysis. In the worst case—when statements are visited in *reverse* order—performance will
+/// be quadratic in the number of statements in the block. The order in which basic blocks are
+/// inspected has no impact on performance.
 pub struct ResultsCursor<'mir, 'tcx, A>
 where
     A: Analysis<'tcx>,
 {
     body: &'mir mir::Body<'tcx>,
-    results: Results<'tcx, A>,
+    results: ResultsHandle<'mir, 'tcx, A>,
     state: A::Domain,
 
     pos: CursorPosition,
@@ -47,13 +93,8 @@ where
         self.body
     }
 
-    /// Unwraps this cursor, returning the underlying `Results`.
-    pub fn into_results(self) -> Results<'tcx, A> {
-        self.results
-    }
-
     /// Returns a new cursor that can inspect `results`.
-    pub fn new(body: &'mir mir::Body<'tcx>, results: Results<'tcx, A>) -> Self {
+    pub fn new(body: &'mir mir::Body<'tcx>, results: ResultsHandle<'mir, 'tcx, A>) -> Self {
         let bottom_value = results.analysis.bottom_value(body);
         ResultsCursor {
             body,
diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs
index 9a5cf9d4e84..566a6b09b2b 100644
--- a/compiler/rustc_mir_dataflow/src/framework/direction.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs
@@ -9,30 +9,35 @@ use super::{Analysis, Effect, EffectIndex, Results, SwitchIntTarget};
 
 pub trait Direction {
     const IS_FORWARD: bool;
-
     const IS_BACKWARD: bool = !Self::IS_FORWARD;
 
-    /// Applies all effects between the given `EffectIndex`s.
-    ///
-    /// `effects.start()` must precede or equal `effects.end()` in this direction.
-    fn apply_effects_in_range<'tcx, A>(
+    /// Called by `iterate_to_fixpoint` during initial analysis computation.
+    fn apply_effects_in_block<'mir, 'tcx, A>(
         analysis: &mut A,
+        body: &mir::Body<'tcx>,
         state: &mut A::Domain,
         block: BasicBlock,
-        block_data: &mir::BasicBlockData<'tcx>,
-        effects: RangeInclusive<EffectIndex>,
+        block_data: &'mir mir::BasicBlockData<'tcx>,
+        propagate: impl FnMut(BasicBlock, &A::Domain),
     ) where
         A: Analysis<'tcx>;
 
-    fn apply_effects_in_block<'mir, 'tcx, A>(
+    /// Called by `ResultsCursor` to recompute the domain value for a location
+    /// in a basic block. Applies all effects between the given `EffectIndex`s.
+    ///
+    /// `effects.start()` must precede or equal `effects.end()` in this direction.
+    fn apply_effects_in_range<'tcx, A>(
         analysis: &mut A,
         state: &mut A::Domain,
         block: BasicBlock,
-        block_data: &'mir mir::BasicBlockData<'tcx>,
-    ) -> TerminatorEdges<'mir, 'tcx>
-    where
+        block_data: &mir::BasicBlockData<'tcx>,
+        effects: RangeInclusive<EffectIndex>,
+    ) where
         A: Analysis<'tcx>;
 
+    /// Called by `ResultsVisitor` to recompute the analysis domain values for
+    /// all locations in a basic block (starting from the entry value stored
+    /// in `Results`) and to visit them with `vis`.
     fn visit_results_in_block<'mir, 'tcx, A>(
         state: &mut A::Domain,
         block: BasicBlock,
@@ -41,16 +46,6 @@ pub trait Direction {
         vis: &mut impl ResultsVisitor<'mir, 'tcx, A>,
     ) where
         A: Analysis<'tcx>;
-
-    fn join_state_into_successors_of<'tcx, A>(
-        analysis: &mut A,
-        body: &mir::Body<'tcx>,
-        exit_state: &mut A::Domain,
-        block: BasicBlock,
-        edges: TerminatorEdges<'_, 'tcx>,
-        propagate: impl FnMut(BasicBlock, &A::Domain),
-    ) where
-        A: Analysis<'tcx>;
 }
 
 /// Dataflow that runs from the exit of a block (terminator), to its entry (the first statement).
@@ -61,23 +56,84 @@ impl Direction for Backward {
 
     fn apply_effects_in_block<'mir, 'tcx, A>(
         analysis: &mut A,
+        body: &mir::Body<'tcx>,
         state: &mut A::Domain,
         block: BasicBlock,
         block_data: &'mir mir::BasicBlockData<'tcx>,
-    ) -> TerminatorEdges<'mir, 'tcx>
-    where
+        mut propagate: impl FnMut(BasicBlock, &A::Domain),
+    ) where
         A: Analysis<'tcx>,
     {
         let terminator = block_data.terminator();
         let location = Location { block, statement_index: block_data.statements.len() };
         analysis.apply_before_terminator_effect(state, terminator, location);
-        let edges = analysis.apply_terminator_effect(state, terminator, location);
+        analysis.apply_terminator_effect(state, terminator, location);
         for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
             let location = Location { block, statement_index };
             analysis.apply_before_statement_effect(state, statement, location);
             analysis.apply_statement_effect(state, statement, location);
         }
-        edges
+
+        let exit_state = state;
+        for pred in body.basic_blocks.predecessors()[block].iter().copied() {
+            match body[pred].terminator().kind {
+                // Apply terminator-specific edge effects.
+                //
+                // FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally.
+                mir::TerminatorKind::Call { destination, target: Some(dest), .. }
+                    if dest == block =>
+                {
+                    let mut tmp = exit_state.clone();
+                    analysis.apply_call_return_effect(
+                        &mut tmp,
+                        pred,
+                        CallReturnPlaces::Call(destination),
+                    );
+                    propagate(pred, &tmp);
+                }
+
+                mir::TerminatorKind::InlineAsm { ref targets, ref operands, .. }
+                    if targets.contains(&block) =>
+                {
+                    let mut tmp = exit_state.clone();
+                    analysis.apply_call_return_effect(
+                        &mut tmp,
+                        pred,
+                        CallReturnPlaces::InlineAsm(operands),
+                    );
+                    propagate(pred, &tmp);
+                }
+
+                mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == block => {
+                    let mut tmp = exit_state.clone();
+                    analysis.apply_call_return_effect(
+                        &mut tmp,
+                        resume,
+                        CallReturnPlaces::Yield(resume_arg),
+                    );
+                    propagate(pred, &tmp);
+                }
+
+                mir::TerminatorKind::SwitchInt { targets: _, ref discr } => {
+                    let mut applier = BackwardSwitchIntEdgeEffectsApplier {
+                        body,
+                        pred,
+                        exit_state,
+                        block,
+                        propagate: &mut propagate,
+                        effects_applied: false,
+                    };
+
+                    analysis.apply_switch_int_edge_effects(pred, discr, &mut applier);
+
+                    if !applier.effects_applied {
+                        propagate(pred, exit_state)
+                    }
+                }
+
+                _ => propagate(pred, exit_state),
+            }
+        }
     }
 
     fn apply_effects_in_range<'tcx, A>(
@@ -170,7 +226,6 @@ impl Direction for Backward {
 
         vis.visit_block_end(state);
 
-        // Terminator
         let loc = Location { block, statement_index: block_data.statements.len() };
         let term = block_data.terminator();
         results.analysis.apply_before_terminator_effect(state, term, loc);
@@ -188,82 +243,13 @@ impl Direction for Backward {
 
         vis.visit_block_start(state);
     }
-
-    fn join_state_into_successors_of<'tcx, A>(
-        analysis: &mut A,
-        body: &mir::Body<'tcx>,
-        exit_state: &mut A::Domain,
-        bb: BasicBlock,
-        _edges: TerminatorEdges<'_, 'tcx>,
-        mut propagate: impl FnMut(BasicBlock, &A::Domain),
-    ) where
-        A: Analysis<'tcx>,
-    {
-        for pred in body.basic_blocks.predecessors()[bb].iter().copied() {
-            match body[pred].terminator().kind {
-                // Apply terminator-specific edge effects.
-                //
-                // FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally.
-                mir::TerminatorKind::Call { destination, target: Some(dest), .. } if dest == bb => {
-                    let mut tmp = exit_state.clone();
-                    analysis.apply_call_return_effect(
-                        &mut tmp,
-                        pred,
-                        CallReturnPlaces::Call(destination),
-                    );
-                    propagate(pred, &tmp);
-                }
-
-                mir::TerminatorKind::InlineAsm { ref targets, ref operands, .. }
-                    if targets.contains(&bb) =>
-                {
-                    let mut tmp = exit_state.clone();
-                    analysis.apply_call_return_effect(
-                        &mut tmp,
-                        pred,
-                        CallReturnPlaces::InlineAsm(operands),
-                    );
-                    propagate(pred, &tmp);
-                }
-
-                mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == bb => {
-                    let mut tmp = exit_state.clone();
-                    analysis.apply_call_return_effect(
-                        &mut tmp,
-                        resume,
-                        CallReturnPlaces::Yield(resume_arg),
-                    );
-                    propagate(pred, &tmp);
-                }
-
-                mir::TerminatorKind::SwitchInt { targets: _, ref discr } => {
-                    let mut applier = BackwardSwitchIntEdgeEffectsApplier {
-                        body,
-                        pred,
-                        exit_state,
-                        bb,
-                        propagate: &mut propagate,
-                        effects_applied: false,
-                    };
-
-                    analysis.apply_switch_int_edge_effects(pred, discr, &mut applier);
-
-                    if !applier.effects_applied {
-                        propagate(pred, exit_state)
-                    }
-                }
-
-                _ => propagate(pred, exit_state),
-            }
-        }
-    }
 }
 
 struct BackwardSwitchIntEdgeEffectsApplier<'mir, 'tcx, D, F> {
     body: &'mir mir::Body<'tcx>,
     pred: BasicBlock,
     exit_state: &'mir mut D,
-    bb: BasicBlock,
+    block: BasicBlock,
     propagate: &'mir mut F,
     effects_applied: bool,
 }
@@ -276,8 +262,8 @@ where
     fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
         assert!(!self.effects_applied);
 
-        let values = &self.body.basic_blocks.switch_sources()[&(self.bb, self.pred)];
-        let targets = values.iter().map(|&value| SwitchIntTarget { value, target: self.bb });
+        let values = &self.body.basic_blocks.switch_sources()[&(self.block, self.pred)];
+        let targets = values.iter().map(|&value| SwitchIntTarget { value, target: self.block });
 
         let mut tmp = None;
         for target in targets {
@@ -298,11 +284,12 @@ impl Direction for Forward {
 
     fn apply_effects_in_block<'mir, 'tcx, A>(
         analysis: &mut A,
+        _body: &mir::Body<'tcx>,
         state: &mut A::Domain,
         block: BasicBlock,
         block_data: &'mir mir::BasicBlockData<'tcx>,
-    ) -> TerminatorEdges<'mir, 'tcx>
-    where
+        mut propagate: impl FnMut(BasicBlock, &A::Domain),
+    ) where
         A: Analysis<'tcx>,
     {
         for (statement_index, statement) in block_data.statements.iter().enumerate() {
@@ -313,7 +300,53 @@ impl Direction for Forward {
         let terminator = block_data.terminator();
         let location = Location { block, statement_index: block_data.statements.len() };
         analysis.apply_before_terminator_effect(state, terminator, location);
-        analysis.apply_terminator_effect(state, terminator, location)
+        let edges = analysis.apply_terminator_effect(state, terminator, location);
+
+        let exit_state = state;
+        match edges {
+            TerminatorEdges::None => {}
+            TerminatorEdges::Single(target) => propagate(target, exit_state),
+            TerminatorEdges::Double(target, unwind) => {
+                propagate(target, exit_state);
+                propagate(unwind, exit_state);
+            }
+            TerminatorEdges::AssignOnReturn { return_, cleanup, place } => {
+                // This must be done *first*, otherwise the unwind path will see the assignments.
+                if let Some(cleanup) = cleanup {
+                    propagate(cleanup, exit_state);
+                }
+
+                if !return_.is_empty() {
+                    analysis.apply_call_return_effect(exit_state, block, place);
+                    for &target in return_ {
+                        propagate(target, exit_state);
+                    }
+                }
+            }
+            TerminatorEdges::SwitchInt { targets, discr } => {
+                let mut applier = ForwardSwitchIntEdgeEffectsApplier {
+                    exit_state,
+                    targets,
+                    propagate,
+                    effects_applied: false,
+                };
+
+                analysis.apply_switch_int_edge_effects(block, discr, &mut applier);
+
+                let ForwardSwitchIntEdgeEffectsApplier {
+                    exit_state,
+                    mut propagate,
+                    effects_applied,
+                    ..
+                } = applier;
+
+                if !effects_applied {
+                    for target in targets.all_targets() {
+                        propagate(*target, exit_state);
+                    }
+                }
+            }
+        }
     }
 
     fn apply_effects_in_range<'tcx, A>(
@@ -351,7 +384,8 @@ impl Direction for Forward {
                 let statement = &block_data.statements[from.statement_index];
                 analysis.apply_statement_effect(state, statement, location);
 
-                // If we only needed to apply the after effect of the statement at `idx`, we are done.
+                // If we only needed to apply the after effect of the statement at `idx`, we are
+                // done.
                 if from == to {
                     return;
                 }
@@ -419,62 +453,6 @@ impl Direction for Forward {
 
         vis.visit_block_end(state);
     }
-
-    fn join_state_into_successors_of<'tcx, A>(
-        analysis: &mut A,
-        _body: &mir::Body<'tcx>,
-        exit_state: &mut A::Domain,
-        bb: BasicBlock,
-        edges: TerminatorEdges<'_, 'tcx>,
-        mut propagate: impl FnMut(BasicBlock, &A::Domain),
-    ) where
-        A: Analysis<'tcx>,
-    {
-        match edges {
-            TerminatorEdges::None => {}
-            TerminatorEdges::Single(target) => propagate(target, exit_state),
-            TerminatorEdges::Double(target, unwind) => {
-                propagate(target, exit_state);
-                propagate(unwind, exit_state);
-            }
-            TerminatorEdges::AssignOnReturn { return_, cleanup, place } => {
-                // This must be done *first*, otherwise the unwind path will see the assignments.
-                if let Some(cleanup) = cleanup {
-                    propagate(cleanup, exit_state);
-                }
-
-                if !return_.is_empty() {
-                    analysis.apply_call_return_effect(exit_state, bb, place);
-                    for &target in return_ {
-                        propagate(target, exit_state);
-                    }
-                }
-            }
-            TerminatorEdges::SwitchInt { targets, discr } => {
-                let mut applier = ForwardSwitchIntEdgeEffectsApplier {
-                    exit_state,
-                    targets,
-                    propagate,
-                    effects_applied: false,
-                };
-
-                analysis.apply_switch_int_edge_effects(bb, discr, &mut applier);
-
-                let ForwardSwitchIntEdgeEffectsApplier {
-                    exit_state,
-                    mut propagate,
-                    effects_applied,
-                    ..
-                } = applier;
-
-                if !effects_applied {
-                    for target in targets.all_targets() {
-                        propagate(*target, exit_state);
-                    }
-                }
-            }
-        }
-    }
 }
 
 struct ForwardSwitchIntEdgeEffectsApplier<'mir, D, F> {
diff --git a/compiler/rustc_mir_dataflow/src/framework/fmt.rs b/compiler/rustc_mir_dataflow/src/framework/fmt.rs
index ed540cd148c..c177d98106f 100644
--- a/compiler/rustc_mir_dataflow/src/framework/fmt.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/fmt.rs
@@ -230,16 +230,3 @@ where
         write!(f, "{}", ctxt.move_data().move_paths[*self])
     }
 }
-
-impl<T, C> DebugWithContext<C> for crate::lattice::Dual<T>
-where
-    T: DebugWithContext<C>,
-{
-    fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        (self.0).fmt_with(ctxt, f)
-    }
-
-    fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        (self.0).fmt_diff_with(&old.0, ctxt, f)
-    }
-}
diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
index 98a4f58cb5d..6e4994af8b4 100644
--- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
@@ -47,20 +47,16 @@ where
 {
     pub(crate) fn new(
         body: &'mir Body<'tcx>,
-        results: Results<'tcx, A>,
+        results: &'mir Results<'tcx, A>,
         style: OutputStyle,
     ) -> Self {
         let reachable = mir::traversal::reachable_as_bitset(body);
-        Formatter { cursor: results.into_results_cursor(body).into(), style, reachable }
+        Formatter { cursor: results.as_results_cursor(body).into(), style, reachable }
     }
 
     fn body(&self) -> &'mir Body<'tcx> {
         self.cursor.borrow().body()
     }
-
-    pub(crate) fn into_results(self) -> Results<'tcx, A> {
-        self.cursor.into_inner().into_results()
-    }
 }
 
 /// A pair of a basic block and an index into that basic blocks `successors`.
diff --git a/compiler/rustc_mir_dataflow/src/framework/lattice.rs b/compiler/rustc_mir_dataflow/src/framework/lattice.rs
index 4d03ee53b7c..6d2a7a099a0 100644
--- a/compiler/rustc_mir_dataflow/src/framework/lattice.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/lattice.rs
@@ -25,8 +25,8 @@
 //!
 //! ## `PartialOrd`
 //!
-//! Given that they represent partially ordered sets, you may be surprised that [`JoinSemiLattice`]
-//! and [`MeetSemiLattice`] do not have [`PartialOrd`] as a supertrait. This
+//! Given that it represents a partially ordered set, you may be surprised that [`JoinSemiLattice`]
+//! does not have [`PartialOrd`] as a supertrait. This
 //! is because most standard library types use lexicographic ordering instead of set inclusion for
 //! their `PartialOrd` impl. Since we do not actually need to compare lattice elements to run a
 //! dataflow analysis, there's no need for a newtype wrapper with a custom `PartialOrd` impl. The
@@ -58,23 +58,6 @@ pub trait JoinSemiLattice: Eq {
     fn join(&mut self, other: &Self) -> bool;
 }
 
-/// A [partially ordered set][poset] that has a [greatest lower bound][glb] for any pair of
-/// elements in the set.
-///
-/// Dataflow analyses only require that their domains implement [`JoinSemiLattice`], not
-/// `MeetSemiLattice`. However, types that will be used as dataflow domains should implement both
-/// so that they can be used with [`Dual`].
-///
-/// [glb]: https://en.wikipedia.org/wiki/Infimum_and_supremum
-/// [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set
-pub trait MeetSemiLattice: Eq {
-    /// Computes the greatest lower bound of two elements, storing the result in `self` and
-    /// returning `true` if `self` has changed.
-    ///
-    /// The lattice meet operator is abbreviated as `∧`.
-    fn meet(&mut self, other: &Self) -> bool;
-}
-
 /// A set that has a "bottom" element, which is less than or equal to any other element.
 pub trait HasBottom {
     const BOTTOM: Self;
@@ -105,17 +88,6 @@ impl JoinSemiLattice for bool {
     }
 }
 
-impl MeetSemiLattice for bool {
-    fn meet(&mut self, other: &Self) -> bool {
-        if let (true, false) = (*self, *other) {
-            *self = false;
-            return true;
-        }
-
-        false
-    }
-}
-
 impl HasBottom for bool {
     const BOTTOM: Self = false;
 
@@ -145,18 +117,6 @@ impl<I: Idx, T: JoinSemiLattice> JoinSemiLattice for IndexVec<I, T> {
     }
 }
 
-impl<I: Idx, T: MeetSemiLattice> MeetSemiLattice for IndexVec<I, T> {
-    fn meet(&mut self, other: &Self) -> bool {
-        assert_eq!(self.len(), other.len());
-
-        let mut changed = false;
-        for (a, b) in iter::zip(self, other) {
-            changed |= a.meet(b);
-        }
-        changed
-    }
-}
-
 /// A `BitSet` represents the lattice formed by the powerset of all possible values of
 /// the index type `T` ordered by inclusion. Equivalently, it is a tuple of "two-point" lattices,
 /// one for each possible value of `T`.
@@ -166,60 +126,12 @@ impl<T: Idx> JoinSemiLattice for BitSet<T> {
     }
 }
 
-impl<T: Idx> MeetSemiLattice for BitSet<T> {
-    fn meet(&mut self, other: &Self) -> bool {
-        self.intersect(other)
-    }
-}
-
 impl<T: Idx> JoinSemiLattice for ChunkedBitSet<T> {
     fn join(&mut self, other: &Self) -> bool {
         self.union(other)
     }
 }
 
-impl<T: Idx> MeetSemiLattice for ChunkedBitSet<T> {
-    fn meet(&mut self, other: &Self) -> bool {
-        self.intersect(other)
-    }
-}
-
-/// The counterpart of a given semilattice `T` using the [inverse order].
-///
-/// The dual of a join-semilattice is a meet-semilattice and vice versa. For example, the dual of a
-/// powerset has the empty set as its top element and the full set as its bottom element and uses
-/// set *intersection* as its join operator.
-///
-/// [inverse order]: https://en.wikipedia.org/wiki/Duality_(order_theory)
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub struct Dual<T>(pub T);
-
-impl<T: Idx> BitSetExt<T> for Dual<BitSet<T>> {
-    fn contains(&self, elem: T) -> bool {
-        self.0.contains(elem)
-    }
-
-    fn union(&mut self, other: &HybridBitSet<T>) {
-        self.0.union(other);
-    }
-
-    fn subtract(&mut self, other: &HybridBitSet<T>) {
-        self.0.subtract(other);
-    }
-}
-
-impl<T: MeetSemiLattice> JoinSemiLattice for Dual<T> {
-    fn join(&mut self, other: &Self) -> bool {
-        self.0.meet(&other.0)
-    }
-}
-
-impl<T: JoinSemiLattice> MeetSemiLattice for Dual<T> {
-    fn meet(&mut self, other: &Self) -> bool {
-        self.0.join(&other.0)
-    }
-}
-
 /// Extends a type `T` with top and bottom elements to make it a partially ordered set in which no
 /// value of `T` is comparable with any other.
 ///
@@ -257,22 +169,6 @@ impl<T: Clone + Eq> JoinSemiLattice for FlatSet<T> {
     }
 }
 
-impl<T: Clone + Eq> MeetSemiLattice for FlatSet<T> {
-    fn meet(&mut self, other: &Self) -> bool {
-        let result = match (&*self, other) {
-            (Self::Bottom, _) | (_, Self::Top) => return false,
-            (Self::Elem(ref a), Self::Elem(ref b)) if a == b => return false,
-
-            (Self::Top, Self::Elem(ref x)) => Self::Elem(x.clone()),
-
-            _ => Self::Bottom,
-        };
-
-        *self = result;
-        true
-    }
-}
-
 impl<T> HasBottom for FlatSet<T> {
     const BOTTOM: Self = Self::Bottom;
 
diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs
index 244dfe26ad3..bb652a75caa 100644
--- a/compiler/rustc_mir_dataflow/src/framework/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs
@@ -8,8 +8,9 @@
 //! The `impls` module contains several examples of dataflow analyses.
 //!
 //! Then call `iterate_to_fixpoint` on your type that impls `Analysis` to get a `Results`. From
-//! there, you can use a `ResultsCursor` to inspect the fixpoint solution to your dataflow problem,
-//! or implement the `ResultsVisitor` interface and use `visit_results`. The following example uses
+//! there, you can use a `ResultsCursor` to inspect the fixpoint solution to your dataflow problem
+//! (good for inspecting a small number of locations), or implement the `ResultsVisitor` interface
+//! and use `visit_results` (good for inspecting many or all locations). The following example uses
 //! the `ResultsCursor` approach.
 //!
 //! ```ignore (cross-crate-imports)
@@ -278,22 +279,17 @@ pub trait Analysis<'tcx> {
         // every iteration.
         let mut state = self.bottom_value(body);
         while let Some(bb) = dirty_queue.pop() {
-            let bb_data = &body[bb];
-
             // Set the state to the entry state of the block.
             // This is equivalent to `state = entry_sets[bb].clone()`,
             // but it saves an allocation, thus improving compile times.
             state.clone_from(&entry_sets[bb]);
 
-            // Apply the block transfer function, using the cached one if it exists.
-            let edges = Self::Direction::apply_effects_in_block(&mut self, &mut state, bb, bb_data);
-
-            Self::Direction::join_state_into_successors_of(
+            Self::Direction::apply_effects_in_block(
                 &mut self,
                 body,
                 &mut state,
                 bb,
-                edges,
+                &body[bb],
                 |target: BasicBlock, state: &Self::Domain| {
                     let set_changed = entry_sets[target].join(state);
                     if set_changed {
@@ -306,14 +302,13 @@ pub trait Analysis<'tcx> {
         let results = Results { analysis: self, entry_sets };
 
         if tcx.sess.opts.unstable_opts.dump_mir_dataflow {
-            let (res, results) = write_graphviz_results(tcx, body, results, pass_name);
+            let res = write_graphviz_results(tcx, body, &results, pass_name);
             if let Err(e) = res {
                 error!("Failed to write graphviz dataflow results: {}", e);
             }
-            results
-        } else {
-            results
         }
+
+        results
     }
 }
 
@@ -378,16 +373,6 @@ impl<T, S: GenKill<T>> GenKill<T> for MaybeReachable<S> {
     }
 }
 
-impl<T: Idx> GenKill<T> for lattice::Dual<BitSet<T>> {
-    fn gen_(&mut self, elem: T) {
-        self.0.insert(elem);
-    }
-
-    fn kill(&mut self, elem: T) {
-        self.0.remove(elem);
-    }
-}
-
 // NOTE: DO NOT CHANGE VARIANT ORDER. The derived `Ord` impls rely on the current order.
 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
 enum Effect {
diff --git a/compiler/rustc_mir_dataflow/src/framework/results.rs b/compiler/rustc_mir_dataflow/src/framework/results.rs
index ff6cafbfbae..8493a7aa44b 100644
--- a/compiler/rustc_mir_dataflow/src/framework/results.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/results.rs
@@ -17,10 +17,13 @@ use super::{Analysis, ResultsCursor, ResultsVisitor, graphviz, visit_results};
 use crate::errors::{
     DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter,
 };
+use crate::framework::cursor::ResultsHandle;
 
 pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as Analysis<'tcx>>::Domain>;
 
-/// A dataflow analysis that has converged to fixpoint.
+/// A dataflow analysis that has converged to fixpoint. It only holds the domain values at the
+/// entry of each basic block. Domain values in other parts of the block are recomputed on the fly
+/// by visitors (i.e. `ResultsCursor`, or `ResultsVisitor` impls).
 #[derive(Clone)]
 pub struct Results<'tcx, A>
 where
@@ -34,12 +37,30 @@ impl<'tcx, A> Results<'tcx, A>
 where
     A: Analysis<'tcx>,
 {
-    /// Creates a `ResultsCursor` that can inspect these `Results`.
+    /// Creates a `ResultsCursor` that can inspect these `Results`. Immutably borrows the `Results`,
+    /// which is appropriate when the `Results` is used outside the cursor.
+    pub fn as_results_cursor<'mir>(
+        &'mir self,
+        body: &'mir mir::Body<'tcx>,
+    ) -> ResultsCursor<'mir, 'tcx, A> {
+        ResultsCursor::new(body, ResultsHandle::Borrowed(self))
+    }
+
+    /// Creates a `ResultsCursor` that can mutate these `Results`. Mutably borrows the `Results`,
+    /// which is appropriate when the `Results` is used outside the cursor.
+    pub fn as_results_cursor_mut<'mir>(
+        &'mir mut self,
+        body: &'mir mir::Body<'tcx>,
+    ) -> ResultsCursor<'mir, 'tcx, A> {
+        ResultsCursor::new(body, ResultsHandle::BorrowedMut(self))
+    }
+
+    /// Creates a `ResultsCursor` that takes ownership of the `Results`.
     pub fn into_results_cursor<'mir>(
         self,
         body: &'mir mir::Body<'tcx>,
     ) -> ResultsCursor<'mir, 'tcx, A> {
-        ResultsCursor::new(body, self)
+        ResultsCursor::new(body, ResultsHandle::Owned(self))
     }
 
     /// Gets the dataflow state for the given block.
@@ -74,9 +95,9 @@ where
 pub(super) fn write_graphviz_results<'tcx, A>(
     tcx: TyCtxt<'tcx>,
     body: &mir::Body<'tcx>,
-    results: Results<'tcx, A>,
+    results: &Results<'tcx, A>,
     pass_name: Option<&'static str>,
-) -> (std::io::Result<()>, Results<'tcx, A>)
+) -> std::io::Result<()>
 where
     A: Analysis<'tcx>,
     A::Domain: DebugWithContext<A>,
@@ -87,7 +108,7 @@ where
     let def_id = body.source.def_id();
     let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else {
         // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
-        return (Ok(()), results);
+        return Ok(());
     };
 
     let file = try {
@@ -104,12 +125,12 @@ where
                 create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
             }
 
-            _ => return (Ok(()), results),
+            _ => return Ok(()),
         }
     };
     let mut file = match file {
         Ok(f) => f,
-        Err(e) => return (Err(e), results),
+        Err(e) => return Err(e),
     };
 
     let style = match attrs.formatter {
@@ -132,7 +153,7 @@ where
         file.write_all(&buf)?;
     };
 
-    (lhs, graphviz.into_results())
+    lhs
 }
 
 #[derive(Default)]
diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs
index 5c7539eed4d..bde41974d47 100644
--- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs
@@ -26,7 +26,9 @@ pub fn visit_results<'mir, 'tcx, A>(
     }
 }
 
-/// A visitor over the results of an `Analysis`.
+/// A visitor over the results of an `Analysis`. Use this when you want to inspect domain values in
+/// many or all locations; use `ResultsCursor` if you want to inspect domain values only in certain
+/// locations.
 pub trait ResultsVisitor<'mir, 'tcx, A>
 where
     A: Analysis<'tcx>,
diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
index 859019fd1f6..cec654cac72 100644
--- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
@@ -15,7 +15,7 @@ use crate::{Analysis, GenKill};
 pub struct MaybeBorrowedLocals;
 
 impl MaybeBorrowedLocals {
-    pub(super) fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T> {
+    pub(super) fn transfer_function<'a, T>(trans: &'a mut T) -> TransferFunction<'a, T> {
         TransferFunction { trans }
     }
 }
@@ -39,7 +39,7 @@ impl<'tcx> Analysis<'tcx> for MaybeBorrowedLocals {
         statement: &Statement<'tcx>,
         location: Location,
     ) {
-        self.transfer_function(trans).visit_statement(statement, location);
+        Self::transfer_function(trans).visit_statement(statement, location);
     }
 
     fn apply_terminator_effect<'mir>(
@@ -48,7 +48,7 @@ impl<'tcx> Analysis<'tcx> for MaybeBorrowedLocals {
         terminator: &'mir Terminator<'tcx>,
         location: Location,
     ) -> TerminatorEdges<'mir, 'tcx> {
-        self.transfer_function(trans).visit_terminator(terminator, location);
+        Self::transfer_function(trans).visit_terminator(terminator, location);
         terminator.edges()
     }
 }
diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs
index 9bb50d1e056..2c10d4b1cd3 100644
--- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs
@@ -12,7 +12,7 @@ use crate::framework::SwitchIntEdgeEffects;
 use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
 use crate::{
     Analysis, GenKill, MaybeReachable, drop_flag_effects, drop_flag_effects_for_function_entry,
-    drop_flag_effects_for_location, lattice, on_all_children_bits, on_lookup_result_bits,
+    drop_flag_effects_for_location, on_all_children_bits, on_lookup_result_bits,
 };
 
 /// `MaybeInitializedPlaces` tracks all places that might be
@@ -42,10 +42,10 @@ use crate::{
 /// }
 /// ```
 ///
-/// To determine whether a place *must* be initialized at a
-/// particular control-flow point, one can take the set-difference
-/// between this data and the data from `MaybeUninitializedPlaces` at the
-/// corresponding control-flow point.
+/// To determine whether a place is *definitely* initialized at a
+/// particular control-flow point, one can take the set-complement
+/// of the data from `MaybeUninitializedPlaces` at the corresponding
+/// control-flow point.
 ///
 /// Similarly, at a given `drop` statement, the set-intersection
 /// between this data and `MaybeUninitializedPlaces` yields the set of
@@ -117,10 +117,10 @@ impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> {
 /// }
 /// ```
 ///
-/// To determine whether a place *must* be uninitialized at a
-/// particular control-flow point, one can take the set-difference
-/// between this data and the data from `MaybeInitializedPlaces` at the
-/// corresponding control-flow point.
+/// To determine whether a place is *definitely* uninitialized at a
+/// particular control-flow point, one can take the set-complement
+/// of the data from `MaybeInitializedPlaces` at the corresponding
+/// control-flow point.
 ///
 /// Similarly, at a given `drop` statement, the set-intersection
 /// between this data and `MaybeInitializedPlaces` yields the set of
@@ -170,57 +170,6 @@ impl<'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
     }
 }
 
-/// `DefinitelyInitializedPlaces` tracks all places that are definitely
-/// initialized upon reaching a particular point in the control flow
-/// for a function.
-///
-/// For example, in code like the following, we have corresponding
-/// dataflow information shown in the right-hand comments.
-///
-/// ```rust
-/// struct S;
-/// fn foo(pred: bool) {                        // definite-init:
-///                                             // {          }
-///     let a = S; let mut b = S; let c; let d; // {a, b      }
-///
-///     if pred {
-///         drop(a);                            // {   b,     }
-///         b = S;                              // {   b,     }
-///
-///     } else {
-///         drop(b);                            // {a,        }
-///         d = S;                              // {a,       d}
-///
-///     }                                       // {          }
-///
-///     c = S;                                  // {       c  }
-/// }
-/// ```
-///
-/// To determine whether a place *may* be uninitialized at a
-/// particular control-flow point, one can take the set-complement
-/// of this data.
-///
-/// Similarly, at a given `drop` statement, the set-difference between
-/// this data and `MaybeInitializedPlaces` yields the set of places
-/// that would require a dynamic drop-flag at that statement.
-pub struct DefinitelyInitializedPlaces<'a, 'tcx> {
-    body: &'a Body<'tcx>,
-    move_data: &'a MoveData<'tcx>,
-}
-
-impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
-    pub fn new(body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self {
-        DefinitelyInitializedPlaces { body, move_data }
-    }
-}
-
-impl<'a, 'tcx> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
-    fn move_data(&self) -> &MoveData<'tcx> {
-        self.move_data
-    }
-}
-
 /// `EverInitializedPlaces` tracks all places that might have ever been
 /// initialized upon reaching a particular point in the control flow
 /// for a function, without an intervening `StorageDead`.
@@ -293,19 +242,6 @@ impl<'tcx> MaybeUninitializedPlaces<'_, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
-    fn update_bits(
-        trans: &mut <Self as Analysis<'tcx>>::Domain,
-        path: MovePathIndex,
-        state: DropFlagState,
-    ) {
-        match state {
-            DropFlagState::Absent => trans.kill(path),
-            DropFlagState::Present => trans.gen_(path),
-        }
-    }
-}
-
 impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
     /// There can be many more `MovePathIndex` than there are locals in a MIR body.
     /// We use a chunked bitset to avoid paying too high a memory footprint.
@@ -554,70 +490,6 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> Analysis<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
-    /// Use set intersection as the join operator.
-    type Domain = lattice::Dual<BitSet<MovePathIndex>>;
-
-    const NAME: &'static str = "definite_init";
-
-    fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
-        // bottom = initialized (start_block_effect counters this at outset)
-        lattice::Dual(BitSet::new_filled(self.move_data().move_paths.len()))
-    }
-
-    // sets on_entry bits for Arg places
-    fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
-        state.0.clear();
-
-        drop_flag_effects_for_function_entry(self.body, self.move_data, |path, s| {
-            assert!(s == DropFlagState::Present);
-            state.0.insert(path);
-        });
-    }
-
-    fn apply_statement_effect(
-        &mut self,
-        trans: &mut Self::Domain,
-        _statement: &mir::Statement<'tcx>,
-        location: Location,
-    ) {
-        drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
-            Self::update_bits(trans, path, s)
-        })
-    }
-
-    fn apply_terminator_effect<'mir>(
-        &mut self,
-        trans: &mut Self::Domain,
-        terminator: &'mir mir::Terminator<'tcx>,
-        location: Location,
-    ) -> TerminatorEdges<'mir, 'tcx> {
-        drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
-            Self::update_bits(trans, path, s)
-        });
-        terminator.edges()
-    }
-
-    fn apply_call_return_effect(
-        &mut self,
-        trans: &mut Self::Domain,
-        _block: mir::BasicBlock,
-        return_places: CallReturnPlaces<'_, 'tcx>,
-    ) {
-        return_places.for_each(|place| {
-            // when a call returns successfully, that means we need to set
-            // the bits for that dest_place to 1 (initialized).
-            on_lookup_result_bits(
-                self.move_data(),
-                self.move_data().rev_lookup.find(place.as_ref()),
-                |mpi| {
-                    trans.gen_(mpi);
-                },
-            );
-        });
-    }
-}
-
 impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
     /// There can be many more `InitIndex` than there are locals in a MIR body.
     /// We use a chunked bitset to avoid paying too high a memory footprint.
diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs
index 9b5bfa9bfa0..b9e194b00c5 100644
--- a/compiler/rustc_mir_dataflow/src/impls/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs
@@ -9,10 +9,11 @@ mod storage_liveness;
 
 pub use self::borrowed_locals::{MaybeBorrowedLocals, borrowed_locals};
 pub use self::initialized::{
-    DefinitelyInitializedPlaces, EverInitializedPlaces, MaybeInitializedPlaces,
-    MaybeUninitializedPlaces,
+    EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
 };
 pub use self::liveness::{
     MaybeLiveLocals, MaybeTransitiveLiveLocals, TransferFunction as LivenessTransferFunction,
 };
-pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageDead, MaybeStorageLive};
+pub use self::storage_liveness::{
+    MaybeRequiresStorage, MaybeStorageDead, MaybeStorageLive, always_storage_live_locals,
+};
diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
index 576289e228a..1aae06d79d3 100644
--- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
@@ -7,6 +7,23 @@ use rustc_middle::mir::*;
 use super::MaybeBorrowedLocals;
 use crate::{Analysis, GenKill, ResultsCursor};
 
+/// The set of locals in a MIR body that do not have `StorageLive`/`StorageDead` annotations.
+///
+/// These locals have fixed storage for the duration of the body.
+pub fn always_storage_live_locals(body: &Body<'_>) -> BitSet<Local> {
+    let mut always_live_locals = BitSet::new_filled(body.local_decls.len());
+
+    for block in &*body.basic_blocks {
+        for statement in &block.statements {
+            if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = statement.kind {
+                always_live_locals.remove(l);
+            }
+        }
+    }
+
+    always_live_locals
+}
+
 pub struct MaybeStorageLive<'a> {
     always_live_locals: Cow<'a, BitSet<Local>>,
 }
@@ -28,10 +45,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageLive<'a> {
     }
 
     fn initialize_start_block(&self, body: &Body<'tcx>, on_entry: &mut Self::Domain) {
-        assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size());
-        for local in self.always_live_locals.iter() {
-            on_entry.insert(local);
-        }
+        on_entry.union(&*self.always_live_locals);
 
         for arg in body.args_iter() {
             on_entry.insert(arg);
@@ -135,7 +149,7 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
         loc: Location,
     ) {
         // If a place is borrowed in a statement, it needs storage for that statement.
-        self.borrowed_locals.mut_analysis().apply_statement_effect(trans, stmt, loc);
+        MaybeBorrowedLocals::transfer_function(trans).visit_statement(stmt, loc);
 
         match &stmt.kind {
             StatementKind::StorageDead(l) => trans.kill(*l),
@@ -180,10 +194,7 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
         loc: Location,
     ) {
         // If a place is borrowed in a terminator, it needs storage for that terminator.
-        self.borrowed_locals
-            .mut_analysis()
-            .transfer_function(trans)
-            .visit_terminator(terminator, loc);
+        MaybeBorrowedLocals::transfer_function(trans).visit_terminator(terminator, loc);
 
         match &terminator.kind {
             TerminatorKind::Call { destination, .. } => {
diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs
index 7ef7c541173..2248972cecc 100644
--- a/compiler/rustc_mir_dataflow/src/lib.rs
+++ b/compiler/rustc_mir_dataflow/src/lib.rs
@@ -25,7 +25,7 @@ pub use self::framework::{
 use self::move_paths::MoveData;
 
 pub mod debuginfo;
-pub mod drop_flag_effects;
+mod drop_flag_effects;
 pub mod elaborate_drops;
 mod errors;
 mod framework;
@@ -33,8 +33,7 @@ pub mod impls;
 pub mod move_paths;
 pub mod points;
 pub mod rustc_peek;
-pub mod storage;
-pub mod un_derefer;
+mod un_derefer;
 pub mod value_analysis;
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs
index 99d0ccde105..34ef8afdde3 100644
--- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs
+++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs
@@ -12,9 +12,7 @@ use crate::errors::{
     PeekMustBePlaceOrRefPlace, StopAfterDataFlowEndedCompilation,
 };
 use crate::framework::BitSetExt;
-use crate::impls::{
-    DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeLiveLocals, MaybeUninitializedPlaces,
-};
+use crate::impls::{MaybeInitializedPlaces, MaybeLiveLocals, MaybeUninitializedPlaces};
 use crate::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex};
 use crate::{Analysis, JoinSemiLattice, ResultsCursor};
 
@@ -56,13 +54,6 @@ pub fn sanity_check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
         sanity_check_via_rustc_peek(tcx, flow_uninits.into_results_cursor(body));
     }
 
-    if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_definite_init).is_some() {
-        let flow_def_inits =
-            DefinitelyInitializedPlaces::new(body, &move_data).iterate_to_fixpoint(tcx, body, None);
-
-        sanity_check_via_rustc_peek(tcx, flow_def_inits.into_results_cursor(body));
-    }
-
     if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() {
         let flow_liveness = MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None);
 
diff --git a/compiler/rustc_mir_dataflow/src/storage.rs b/compiler/rustc_mir_dataflow/src/storage.rs
deleted file mode 100644
index e5a0e1d312e..00000000000
--- a/compiler/rustc_mir_dataflow/src/storage.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-use rustc_index::bit_set::BitSet;
-use rustc_middle::mir::{self, Local};
-
-/// The set of locals in a MIR body that do not have `StorageLive`/`StorageDead` annotations.
-///
-/// These locals have fixed storage for the duration of the body.
-pub fn always_storage_live_locals(body: &mir::Body<'_>) -> BitSet<Local> {
-    let mut always_live_locals = BitSet::new_filled(body.local_decls.len());
-
-    for block in &*body.basic_blocks {
-        for statement in &block.statements {
-            use mir::StatementKind::{StorageDead, StorageLive};
-            if let StorageLive(l) | StorageDead(l) = statement.kind {
-                always_live_locals.remove(l);
-            }
-        }
-    }
-
-    always_live_locals
-}
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index 8295a806d71..9b9aeccf890 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -70,8 +70,8 @@ use rustc_middle::ty::{
 use rustc_middle::{bug, span_bug};
 use rustc_mir_dataflow::impls::{
     MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive,
+    always_storage_live_locals,
 };
-use rustc_mir_dataflow::storage::always_storage_live_locals;
 use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor};
 use rustc_span::Span;
 use rustc_span::def_id::{DefId, LocalDefId};
@@ -676,12 +676,11 @@ fn locals_live_across_suspend_points<'tcx>(
 
     let mut borrowed_locals_cursor = borrowed_locals_results.clone().into_results_cursor(body);
 
-    // Calculate the MIR locals that we actually need to keep storage around
-    // for.
-    let mut requires_storage_cursor =
+    // Calculate the MIR locals that we need to keep storage around for.
+    let mut requires_storage_results =
         MaybeRequiresStorage::new(borrowed_locals_results.into_results_cursor(body))
-            .iterate_to_fixpoint(tcx, body, None)
-            .into_results_cursor(body);
+            .iterate_to_fixpoint(tcx, body, None);
+    let mut requires_storage_cursor = requires_storage_results.as_results_cursor_mut(body);
 
     // Calculate the liveness of MIR locals ignoring borrows.
     let mut liveness =
@@ -697,8 +696,7 @@ fn locals_live_across_suspend_points<'tcx>(
             let loc = Location { block, statement_index: data.statements.len() };
 
             liveness.seek_to_block_end(block);
-            let mut live_locals: BitSet<_> = BitSet::new_empty(body.local_decls.len());
-            live_locals.union(liveness.get());
+            let mut live_locals = liveness.get().clone();
 
             if !movable {
                 // The `liveness` variable contains the liveness of MIR locals ignoring borrows.
@@ -754,7 +752,7 @@ fn locals_live_across_suspend_points<'tcx>(
         body,
         &saved_locals,
         always_live_locals.clone(),
-        requires_storage_cursor.into_results(),
+        requires_storage_results,
     );
 
     LivenessInfo {
diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs
index 9c74b2f0839..8f977d2979e 100644
--- a/compiler/rustc_mir_transform/src/dest_prop.rs
+++ b/compiler/rustc_mir_transform/src/dest_prop.rs
@@ -217,11 +217,6 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
                 else {
                     continue;
                 };
-                if !tcx.consider_optimizing(|| {
-                    format!("{} round {}", tcx.def_path_str(def_id), round_count)
-                }) {
-                    break;
-                }
 
                 // Replace `src` by `dest` everywhere.
                 merges.insert(*src, *dest);
diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
index 704ed508b22..17c8348140a 100644
--- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
+++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
@@ -108,10 +108,6 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch {
             let parent = BasicBlock::from_usize(i);
             let Some(opt_data) = evaluate_candidate(tcx, body, parent) else { continue };
 
-            if !tcx.consider_optimizing(|| format!("EarlyOtherwiseBranch {opt_data:?}")) {
-                break;
-            }
-
             trace!("SUCCESS: found optimization possibility to apply: {opt_data:?}");
 
             should_cleanup = true;
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 00f6c3845d4..0878fa26a92 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -210,12 +210,6 @@ impl<'tcx> Inliner<'tcx> {
         let callee_body = try_instance_mir(self.tcx, callsite.callee.def)?;
         self.check_mir_body(callsite, callee_body, callee_attrs, cross_crate_inlinable)?;
 
-        if !self.tcx.consider_optimizing(|| {
-            format!("Inline {:?} into {:?}", callsite.callee, caller_body.source)
-        }) {
-            return Err("optimization fuel exhausted");
-        }
-
         let Ok(callee_body) = callsite.callee.try_instantiate_mir_and_normalize_erasing_regions(
             self.tcx,
             self.typing_env,
diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs
index 3352d583f2c..a6ba2f32d32 100644
--- a/compiler/rustc_mir_transform/src/instsimplify.rs
+++ b/compiler/rustc_mir_transform/src/instsimplify.rs
@@ -7,8 +7,8 @@ use rustc_middle::bug;
 use rustc_middle::mir::*;
 use rustc_middle::ty::layout::ValidityRequirement;
 use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, layout};
-use rustc_span::sym;
 use rustc_span::symbol::Symbol;
+use rustc_span::{DUMMY_SP, sym};
 
 use crate::simplify::simplify_duplicate_switch_targets;
 use crate::take_array;
@@ -43,12 +43,12 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify {
                 match statement.kind {
                     StatementKind::Assign(box (_place, ref mut rvalue)) => {
                         if !preserve_ub_checks {
-                            ctx.simplify_ub_check(&statement.source_info, rvalue);
+                            ctx.simplify_ub_check(rvalue);
                         }
-                        ctx.simplify_bool_cmp(&statement.source_info, rvalue);
-                        ctx.simplify_ref_deref(&statement.source_info, rvalue);
-                        ctx.simplify_len(&statement.source_info, rvalue);
-                        ctx.simplify_ptr_aggregate(&statement.source_info, rvalue);
+                        ctx.simplify_bool_cmp(rvalue);
+                        ctx.simplify_ref_deref(rvalue);
+                        ctx.simplify_len(rvalue);
+                        ctx.simplify_ptr_aggregate(rvalue);
                         ctx.simplify_cast(rvalue);
                     }
                     _ => {}
@@ -70,23 +70,8 @@ struct InstSimplifyContext<'a, 'tcx> {
 }
 
 impl<'tcx> InstSimplifyContext<'_, 'tcx> {
-    fn should_simplify(&self, source_info: &SourceInfo, rvalue: &Rvalue<'tcx>) -> bool {
-        self.should_simplify_custom(source_info, "Rvalue", rvalue)
-    }
-
-    fn should_simplify_custom(
-        &self,
-        source_info: &SourceInfo,
-        label: &str,
-        value: impl std::fmt::Debug,
-    ) -> bool {
-        self.tcx.consider_optimizing(|| {
-            format!("InstSimplify - {label}: {value:?} SourceInfo: {source_info:?}")
-        })
-    }
-
     /// Transform boolean comparisons into logical operations.
-    fn simplify_bool_cmp(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
+    fn simplify_bool_cmp(&self, rvalue: &mut Rvalue<'tcx>) {
         match rvalue {
             Rvalue::BinaryOp(op @ (BinOp::Eq | BinOp::Ne), box (a, b)) => {
                 let new = match (op, self.try_eval_bool(a), self.try_eval_bool(b)) {
@@ -117,9 +102,7 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
                     _ => None,
                 };
 
-                if let Some(new) = new
-                    && self.should_simplify(source_info, rvalue)
-                {
+                if let Some(new) = new {
                     *rvalue = new;
                 }
             }
@@ -134,17 +117,13 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
     }
 
     /// Transform `&(*a)` ==> `a`.
-    fn simplify_ref_deref(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
+    fn simplify_ref_deref(&self, rvalue: &mut Rvalue<'tcx>) {
         if let Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) = rvalue {
             if let Some((base, ProjectionElem::Deref)) = place.as_ref().last_projection() {
                 if rvalue.ty(self.local_decls, self.tcx) != base.ty(self.local_decls, self.tcx).ty {
                     return;
                 }
 
-                if !self.should_simplify(source_info, rvalue) {
-                    return;
-                }
-
                 *rvalue = Rvalue::Use(Operand::Copy(Place {
                     local: base.local,
                     projection: self.tcx.mk_place_elems(base.projection),
@@ -154,36 +133,24 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
     }
 
     /// Transform `Len([_; N])` ==> `N`.
-    fn simplify_len(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
+    fn simplify_len(&self, rvalue: &mut Rvalue<'tcx>) {
         if let Rvalue::Len(ref place) = *rvalue {
             let place_ty = place.ty(self.local_decls, self.tcx).ty;
             if let ty::Array(_, len) = *place_ty.kind() {
-                if !self.should_simplify(source_info, rvalue) {
-                    return;
-                }
-
                 let const_ = Const::from_ty_const(len, self.tcx.types.usize, self.tcx);
-                let constant = ConstOperand { span: source_info.span, const_, user_ty: None };
+                let constant = ConstOperand { span: DUMMY_SP, const_, user_ty: None };
                 *rvalue = Rvalue::Use(Operand::Constant(Box::new(constant)));
             }
         }
     }
 
     /// Transform `Aggregate(RawPtr, [p, ()])` ==> `Cast(PtrToPtr, p)`.
-    fn simplify_ptr_aggregate(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
+    fn simplify_ptr_aggregate(&self, rvalue: &mut Rvalue<'tcx>) {
         if let Rvalue::Aggregate(box AggregateKind::RawPtr(pointee_ty, mutability), fields) = rvalue
         {
             let meta_ty = fields.raw[1].ty(self.local_decls, self.tcx);
             if meta_ty.is_unit() {
                 // The mutable borrows we're holding prevent printing `rvalue` here
-                if !self.should_simplify_custom(
-                    source_info,
-                    "Aggregate::RawPtr",
-                    (&pointee_ty, *mutability, &fields),
-                ) {
-                    return;
-                }
-
                 let mut fields = std::mem::take(fields);
                 let _meta = fields.pop().unwrap();
                 let data = fields.pop().unwrap();
@@ -193,10 +160,10 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
         }
     }
 
-    fn simplify_ub_check(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
+    fn simplify_ub_check(&self, rvalue: &mut Rvalue<'tcx>) {
         if let Rvalue::NullaryOp(NullOp::UbChecks, _) = *rvalue {
             let const_ = Const::from_bool(self.tcx, self.tcx.sess.ub_checks());
-            let constant = ConstOperand { span: source_info.span, const_, user_ty: None };
+            let constant = ConstOperand { span: DUMMY_SP, const_, user_ty: None };
             *rvalue = Rvalue::Use(Operand::Constant(Box::new(constant)));
         }
     }
@@ -284,16 +251,6 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
             return;
         }
 
-        if !self.tcx.consider_optimizing(|| {
-            format!(
-                "InstSimplify - Call: {:?} SourceInfo: {:?}",
-                (fn_def_id, fn_args),
-                terminator.source_info
-            )
-        }) {
-            return;
-        }
-
         let Ok([arg]) = take_array(args) else { return };
         let Some(arg_place) = arg.node.place() else { return };
 
diff --git a/compiler/rustc_mir_transform/src/lint.rs b/compiler/rustc_mir_transform/src/lint.rs
index d8ff1cfc90b..29e762af8de 100644
--- a/compiler/rustc_mir_transform/src/lint.rs
+++ b/compiler/rustc_mir_transform/src/lint.rs
@@ -9,8 +9,7 @@ use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::visit::{PlaceContext, Visitor};
 use rustc_middle::mir::*;
 use rustc_middle::ty::TyCtxt;
-use rustc_mir_dataflow::impls::{MaybeStorageDead, MaybeStorageLive};
-use rustc_mir_dataflow::storage::always_storage_live_locals;
+use rustc_mir_dataflow::impls::{MaybeStorageDead, MaybeStorageLive, always_storage_live_locals};
 use rustc_mir_dataflow::{Analysis, ResultsCursor};
 
 pub(super) fn lint_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, when: String) {
diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs
index ff027680c49..20e2a65b311 100644
--- a/compiler/rustc_mir_transform/src/match_branches.rs
+++ b/compiler/rustc_mir_transform/src/match_branches.rs
@@ -18,16 +18,11 @@ impl<'tcx> crate::MirPass<'tcx> for MatchBranchSimplification {
     }
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-        let def_id = body.source.def_id();
         let typing_env = body.typing_env(tcx);
         let mut should_cleanup = false;
         for i in 0..body.basic_blocks.len() {
             let bbs = &*body.basic_blocks;
             let bb_idx = BasicBlock::from_usize(i);
-            if !tcx.consider_optimizing(|| format!("MatchBranchSimplification {def_id:?} ")) {
-                continue;
-            }
-
             match bbs[bb_idx].terminator().kind {
                 TerminatorKind::SwitchInt {
                     discr: ref _discr @ (Operand::Copy(_) | Operand::Move(_)),
diff --git a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs
index b6d6ef5de1d..a9227524ce5 100644
--- a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs
+++ b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs
@@ -14,10 +14,9 @@ impl<'tcx> crate::MirPass<'tcx> for MultipleReturnTerminators {
         sess.mir_opt_level() >= 4
     }
 
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+    fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         // find basic blocks with no statement and a return terminator
         let mut bbs_simple_returns = BitSet::new_empty(body.basic_blocks.len());
-        let def_id = body.source.def_id();
         let bbs = body.basic_blocks_mut();
         for idx in bbs.indices() {
             if bbs[idx].statements.is_empty()
@@ -28,10 +27,6 @@ impl<'tcx> crate::MirPass<'tcx> for MultipleReturnTerminators {
         }
 
         for bb in bbs {
-            if !tcx.consider_optimizing(|| format!("MultipleReturnTerminators {def_id:?} ")) {
-                break;
-            }
-
             if let TerminatorKind::Goto { target } = bb.terminator().kind {
                 if bbs_simple_returns.contains(target) {
                     bb.terminator_mut().kind = TerminatorKind::Return;
diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs
index 98fa149e2bc..cd026ed6806 100644
--- a/compiler/rustc_mir_transform/src/nrvo.rs
+++ b/compiler/rustc_mir_transform/src/nrvo.rs
@@ -45,10 +45,6 @@ impl<'tcx> crate::MirPass<'tcx> for RenameReturnPlace {
             return;
         };
 
-        if !tcx.consider_optimizing(|| format!("RenameReturnPlace {def_id:?}")) {
-            return;
-        }
-
         debug!(
             "`{:?}` was eligible for NRVO, making {:?} the return place",
             def_id, returned_local
diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs
index af438ac2177..96bcdfa6fac 100644
--- a/compiler/rustc_mir_transform/src/ref_prop.rs
+++ b/compiler/rustc_mir_transform/src/ref_prop.rs
@@ -8,8 +8,7 @@ use rustc_middle::mir::visit::*;
 use rustc_middle::mir::*;
 use rustc_middle::ty::TyCtxt;
 use rustc_mir_dataflow::Analysis;
-use rustc_mir_dataflow::impls::MaybeStorageDead;
-use rustc_mir_dataflow::storage::always_storage_live_locals;
+use rustc_mir_dataflow::impls::{MaybeStorageDead, always_storage_live_locals};
 use tracing::{debug, instrument};
 
 use crate::ssa::{SsaLocals, StorageLiveLocals};
diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
index a535be798cd..e335051d656 100644
--- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
+++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
@@ -26,11 +26,6 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUnneededDrops {
                 if ty.ty.needs_drop(tcx, typing_env) {
                     continue;
                 }
-                if !tcx.consider_optimizing(|| {
-                    format!("RemoveUnneededDrops {:?}", body.source.def_id())
-                }) {
-                    continue;
-                }
                 debug!("SUCCESS: replacing `drop` with goto({:?})", target);
                 terminator.kind = TerminatorKind::Goto { target };
                 should_simplify = true;
diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs
index 6fd70fbe9b0..64e183bcbc0 100644
--- a/compiler/rustc_mir_transform/src/remove_zsts.rs
+++ b/compiler/rustc_mir_transform/src/remove_zsts.rs
@@ -17,10 +17,6 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveZsts {
             return;
         }
 
-        if !tcx.consider_optimizing(|| format!("RemoveZsts - {:?}", body.source.def_id())) {
-            return;
-        }
-
         let typing_env = body.typing_env(tcx);
         let local_decls = &body.local_decls;
         let mut replacer = Replacer { tcx, typing_env, local_decls };
@@ -94,16 +90,12 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
         }
     }
 
-    fn visit_operand(&mut self, operand: &mut Operand<'tcx>, loc: Location) {
+    fn visit_operand(&mut self, operand: &mut Operand<'tcx>, _: Location) {
         if let Operand::Constant(_) = operand {
             return;
         }
         let op_ty = operand.ty(self.local_decls, self.tcx);
-        if self.known_to_be_zst(op_ty)
-            && self.tcx.consider_optimizing(|| {
-                format!("RemoveZsts - Operand: {operand:?} Location: {loc:?}")
-            })
-        {
+        if self.known_to_be_zst(op_ty) {
             *operand = Operand::Constant(Box::new(self.make_zst(op_ty)))
         }
     }
diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs
index 9cd32459c7b..734703ec78b 100644
--- a/compiler/rustc_mir_transform/src/unreachable_prop.rs
+++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs
@@ -43,12 +43,6 @@ impl crate::MirPass<'_> for UnreachablePropagation {
             }
         }
 
-        if !tcx
-            .consider_optimizing(|| format!("UnreachablePropagation {:?} ", body.source.def_id()))
-        {
-            return;
-        }
-
         patch.apply(body);
 
         // We do want do keep some unreachable blocks, but make them empty.
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index 5023e83bd67..8db3b174a89 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -18,6 +18,7 @@ use rustc_span::symbol::Symbol;
 use rustc_span::{BytePos, Pos, Span};
 use tracing::debug;
 
+use crate::lexer::diagnostics::TokenTreeDiagInfo;
 use crate::lexer::unicode_chars::UNICODE_ARRAY;
 use crate::{errors, make_unclosed_delims_error};
 
@@ -56,7 +57,7 @@ pub(crate) fn lex_token_trees<'psess, 'src>(
     }
 
     let cursor = Cursor::new(src);
-    let string_reader = StringReader {
+    let mut lexer = Lexer {
         psess,
         start_pos,
         pos: start_pos,
@@ -65,34 +66,31 @@ pub(crate) fn lex_token_trees<'psess, 'src>(
         override_span,
         nbsp_is_whitespace: false,
         last_lifetime: None,
+        token: Token::dummy(),
+        diag_info: TokenTreeDiagInfo::default(),
     };
-    let (stream, res, unmatched_delims) =
-        tokentrees::TokenTreesReader::lex_all_token_trees(string_reader);
-    match res {
-        Ok(()) if unmatched_delims.is_empty() => Ok(stream),
-        _ => {
-            // Return error if there are unmatched delimiters or unclosed delimiters.
-            // We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch
-            // because the delimiter mismatch is more likely to be the root cause of error
-
-            let mut buffer = Vec::with_capacity(1);
-            for unmatched in unmatched_delims {
-                if let Some(err) = make_unclosed_delims_error(unmatched, psess) {
-                    buffer.push(err);
-                }
-            }
-            if let Err(errs) = res {
-                // Add unclosing delimiter or diff marker errors
-                for err in errs {
-                    buffer.push(err);
-                }
-            }
-            Err(buffer)
+    let (_open_spacing, stream, res) = lexer.lex_token_trees(/* is_delimited */ false);
+    let unmatched_delims = lexer.diag_info.unmatched_delims;
+
+    if res.is_ok() && unmatched_delims.is_empty() {
+        Ok(stream)
+    } else {
+        // Return error if there are unmatched delimiters or unclosed delimiters.
+        // We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch
+        // because the delimiter mismatch is more likely to be the root cause of error
+        let mut buffer: Vec<_> = unmatched_delims
+            .into_iter()
+            .filter_map(|unmatched_delim| make_unclosed_delims_error(unmatched_delim, psess))
+            .collect();
+        if let Err(errs) = res {
+            // Add unclosing delimiter or diff marker errors
+            buffer.extend(errs);
         }
+        Err(buffer)
     }
 }
 
-struct StringReader<'psess, 'src> {
+struct Lexer<'psess, 'src> {
     psess: &'psess ParseSess,
     /// Initial position, read-only.
     start_pos: BytePos,
@@ -111,9 +109,14 @@ struct StringReader<'psess, 'src> {
     /// Track the `Span` for the leading `'` of the last lifetime. Used for
     /// diagnostics to detect possible typo where `"` was meant.
     last_lifetime: Option<Span>,
+
+    /// The current token.
+    token: Token,
+
+    diag_info: TokenTreeDiagInfo,
 }
 
-impl<'psess, 'src> StringReader<'psess, 'src> {
+impl<'psess, 'src> Lexer<'psess, 'src> {
     fn dcx(&self) -> DiagCtxtHandle<'psess> {
         self.psess.dcx()
     }
@@ -124,7 +127,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
 
     /// Returns the next token, paired with a bool indicating if the token was
     /// preceded by whitespace.
-    fn next_token(&mut self) -> (Token, bool) {
+    fn next_token_from_cursor(&mut self) -> (Token, bool) {
         let mut preceded_by_whitespace = false;
         let mut swallow_next_invalid = 0;
         // Skip trivial (whitespace & comments) tokens
@@ -231,7 +234,8 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
                         .push(span);
                     token::Ident(sym, IdentIsRaw::No)
                 }
-                // split up (raw) c string literals to an ident and a string literal when edition < 2021.
+                // split up (raw) c string literals to an ident and a string literal when edition <
+                // 2021.
                 rustc_lexer::TokenKind::Literal {
                     kind: kind @ (LiteralKind::CStr { .. } | LiteralKind::RawCStr { .. }),
                     suffix_start: _,
@@ -252,7 +256,9 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
                     let prefix_span = self.mk_sp(start, lit_start);
                     return (Token::new(self.ident(start), prefix_span), preceded_by_whitespace);
                 }
-                rustc_lexer::TokenKind::GuardedStrPrefix => self.maybe_report_guarded_str(start, str_before),
+                rustc_lexer::TokenKind::GuardedStrPrefix => {
+                    self.maybe_report_guarded_str(start, str_before)
+                }
                 rustc_lexer::TokenKind::Literal { kind, suffix_start } => {
                     let suffix_start = start + BytePos(suffix_start);
                     let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind);
@@ -296,13 +302,20 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
                     if prefix_span.at_least_rust_2021() {
                         let span = self.mk_sp(start, self.pos);
 
-                        let lifetime_name_without_tick = Symbol::intern(&self.str_from(ident_start));
+                        let lifetime_name_without_tick =
+                            Symbol::intern(&self.str_from(ident_start));
                         if !lifetime_name_without_tick.can_be_raw() {
-                            self.dcx().emit_err(errors::CannotBeRawLifetime { span, ident: lifetime_name_without_tick });
+                            self.dcx().emit_err(
+                                errors::CannotBeRawLifetime {
+                                    span,
+                                    ident: lifetime_name_without_tick
+                                }
+                            );
                         }
 
                         // Put the `'` back onto the lifetime name.
-                        let mut lifetime_name = String::with_capacity(lifetime_name_without_tick.as_str().len() + 1);
+                        let mut lifetime_name =
+                            String::with_capacity(lifetime_name_without_tick.as_str().len() + 1);
                         lifetime_name.push('\'');
                         lifetime_name += lifetime_name_without_tick.as_str();
                         let sym = Symbol::intern(&lifetime_name);
diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs
index 7b21ffacc84..c6c9eb3b0b2 100644
--- a/compiler/rustc_parse/src/lexer/tokentrees.rs
+++ b/compiler/rustc_parse/src/lexer/tokentrees.rs
@@ -4,41 +4,19 @@ use rustc_ast_pretty::pprust::token_to_string;
 use rustc_errors::{Applicability, PErr};
 use rustc_span::symbol::kw;
 
-use super::diagnostics::{
-    TokenTreeDiagInfo, report_suspicious_mismatch_block, same_indentation_level,
-};
-use super::{StringReader, UnmatchedDelim};
+use super::diagnostics::{report_suspicious_mismatch_block, same_indentation_level};
+use super::{Lexer, UnmatchedDelim};
 use crate::Parser;
 
-pub(super) struct TokenTreesReader<'psess, 'src> {
-    string_reader: StringReader<'psess, 'src>,
-    /// The "next" token, which has been obtained from the `StringReader` but
-    /// not yet handled by the `TokenTreesReader`.
-    token: Token,
-    diag_info: TokenTreeDiagInfo,
-}
-
-impl<'psess, 'src> TokenTreesReader<'psess, 'src> {
-    pub(super) fn lex_all_token_trees(
-        string_reader: StringReader<'psess, 'src>,
-    ) -> (TokenStream, Result<(), Vec<PErr<'psess>>>, Vec<UnmatchedDelim>) {
-        let mut tt_reader = TokenTreesReader {
-            string_reader,
-            token: Token::dummy(),
-            diag_info: TokenTreeDiagInfo::default(),
-        };
-        let (_open_spacing, stream, res) = tt_reader.lex_token_trees(/* is_delimited */ false);
-        (stream, res, tt_reader.diag_info.unmatched_delims)
-    }
-
+impl<'psess, 'src> Lexer<'psess, 'src> {
     // Lex into a token stream. The `Spacing` in the result is that of the
     // opening delimiter.
-    fn lex_token_trees(
+    pub(super) fn lex_token_trees(
         &mut self,
         is_delimited: bool,
     ) -> (Spacing, TokenStream, Result<(), Vec<PErr<'psess>>>) {
         // Move past the opening delimiter.
-        let (_, open_spacing) = self.bump(false);
+        let open_spacing = self.bump_minimal();
 
         let mut buf = Vec::new();
         loop {
@@ -71,7 +49,7 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> {
                 }
                 _ => {
                     // Get the next normal token.
-                    let (this_tok, this_spacing) = self.bump(true);
+                    let (this_tok, this_spacing) = self.bump();
                     buf.push(TokenTree::Token(this_tok, this_spacing));
                 }
             }
@@ -80,7 +58,7 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> {
 
     fn eof_err(&mut self) -> PErr<'psess> {
         let msg = "this file contains an unclosed delimiter";
-        let mut err = self.string_reader.dcx().struct_span_err(self.token.span, msg);
+        let mut err = self.dcx().struct_span_err(self.token.span, msg);
 
         let unclosed_delimiter_show_limit = 5;
         let len = usize::min(unclosed_delimiter_show_limit, self.diag_info.open_braces.len());
@@ -110,7 +88,7 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> {
             report_suspicious_mismatch_block(
                 &mut err,
                 &self.diag_info,
-                self.string_reader.psess.source_map(),
+                self.psess.source_map(),
                 *delim,
             )
         }
@@ -136,7 +114,7 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> {
 
         // Expand to cover the entire delimited token tree.
         let delim_span = DelimSpan::from_pair(pre_span, self.token.span);
-        let sm = self.string_reader.psess.source_map();
+        let sm = self.psess.source_map();
 
         let close_spacing = match self.token.kind {
             // Correct delimiter.
@@ -160,7 +138,7 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> {
                 }
 
                 // Move past the closing delimiter.
-                self.bump(false).1
+                self.bump_minimal()
             }
             // Incorrect delimiter.
             token::CloseDelim(close_delim) => {
@@ -203,7 +181,7 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> {
                 //     bar(baz(
                 // }  // Incorrect delimiter but matches the earlier `{`
                 if !self.diag_info.open_braces.iter().any(|&(b, _)| b == close_delim) {
-                    self.bump(false).1
+                    self.bump_minimal()
                 } else {
                     // The choice of value here doesn't matter.
                     Spacing::Alone
@@ -225,14 +203,14 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> {
     }
 
     // Move on to the next token, returning the current token and its spacing.
-    // Will glue adjacent single-char tokens together if `glue` is set.
-    fn bump(&mut self, glue: bool) -> (Token, Spacing) {
+    // Will glue adjacent single-char tokens together.
+    fn bump(&mut self) -> (Token, Spacing) {
         let (this_spacing, next_tok) = loop {
-            let (next_tok, is_next_tok_preceded_by_whitespace) = self.string_reader.next_token();
+            let (next_tok, is_next_tok_preceded_by_whitespace) = self.next_token_from_cursor();
 
             if is_next_tok_preceded_by_whitespace {
                 break (Spacing::Alone, next_tok);
-            } else if glue && let Some(glued) = self.token.glue(&next_tok) {
+            } else if let Some(glued) = self.token.glue(&next_tok) {
                 self.token = glued;
             } else {
                 let this_spacing = if next_tok.is_punct() {
@@ -249,6 +227,26 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> {
         (this_tok, this_spacing)
     }
 
+    // Cut-down version of `bump` used when the token kind is known in advance.
+    fn bump_minimal(&mut self) -> Spacing {
+        let (next_tok, is_next_tok_preceded_by_whitespace) = self.next_token_from_cursor();
+
+        let this_spacing = if is_next_tok_preceded_by_whitespace {
+            Spacing::Alone
+        } else {
+            if next_tok.is_punct() {
+                Spacing::Joint
+            } else if next_tok == token::Eof {
+                Spacing::Alone
+            } else {
+                Spacing::JointHidden
+            }
+        };
+
+        self.token = next_tok;
+        this_spacing
+    }
+
     fn unclosed_delim_err(
         &mut self,
         tts: TokenStream,
@@ -256,7 +254,7 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> {
     ) -> Vec<PErr<'psess>> {
         // If there are unclosed delims, see if there are diff markers and if so, point them
         // out instead of complaining about the unclosed delims.
-        let mut parser = Parser::new(self.string_reader.psess, tts, None);
+        let mut parser = Parser::new(self.psess, tts, None);
         let mut diff_errs = vec![];
         // Suggest removing a `{` we think appears in an `if`/`while` condition.
         // We want to suggest removing a `{` only if we think we're in an `if`/`while` condition,
@@ -314,14 +312,9 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> {
         // An unexpected closing delimiter (i.e., there is no matching opening delimiter).
         let token_str = token_to_string(&self.token);
         let msg = format!("unexpected closing delimiter: `{token_str}`");
-        let mut err = self.string_reader.dcx().struct_span_err(self.token.span, msg);
+        let mut err = self.dcx().struct_span_err(self.token.span, msg);
 
-        report_suspicious_mismatch_block(
-            &mut err,
-            &self.diag_info,
-            self.string_reader.psess.source_map(),
-            delim,
-        );
+        report_suspicious_mismatch_block(&mut err, &self.diag_info, self.psess.source_map(), delim);
         err.span_label(self.token.span, "unexpected closing delimiter");
         err
     }
diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs
index d78b3664b1e..42eef27803e 100644
--- a/compiler/rustc_parse/src/lexer/unicode_chars.rs
+++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs
@@ -4,7 +4,7 @@
 use rustc_span::symbol::kw;
 use rustc_span::{BytePos, Pos, Span};
 
-use super::StringReader;
+use super::Lexer;
 use crate::errors::TokenSubstitution;
 use crate::token::{self, Delimiter};
 
@@ -338,7 +338,7 @@ const ASCII_ARRAY: &[(&str, &str, Option<token::TokenKind>)] = &[
 ];
 
 pub(super) fn check_for_substitution(
-    reader: &StringReader<'_, '_>,
+    lexer: &Lexer<'_, '_>,
     pos: BytePos,
     ch: char,
     count: usize,
@@ -351,11 +351,11 @@ pub(super) fn check_for_substitution(
 
     let Some((_, ascii_name, token)) = ASCII_ARRAY.iter().find(|&&(s, _, _)| s == ascii_str) else {
         let msg = format!("substitution character not found for '{ch}'");
-        reader.dcx().span_bug(span, msg);
+        lexer.dcx().span_bug(span, msg);
     };
 
     // special help suggestion for "directed" double quotes
-    let sugg = if let Some(s) = peek_delimited(&reader.src[reader.src_index(pos)..], '“', '”') {
+    let sugg = if let Some(s) = peek_delimited(&lexer.src[lexer.src_index(pos)..], '“', '”') {
         let span = Span::with_root_ctxt(
             pos,
             pos + Pos::from_usize('“'.len_utf8() + s.len() + '”'.len_utf8()),
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index 5aebe716b0a..76ecb77d750 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -1,6 +1,7 @@
 use ast::token::Delimiter;
 use rustc_ast::{
-    self as ast, AttrVec, GenericBounds, GenericParam, GenericParamKind, TyKind, WhereClause, token,
+    self as ast, AttrVec, DUMMY_NODE_ID, GenericBounds, GenericParam, GenericParamKind, TyKind,
+    WhereClause, token,
 };
 use rustc_errors::{Applicability, PResult};
 use rustc_span::Span;
@@ -14,8 +15,8 @@ use crate::errors::{
     WhereClauseBeforeTupleStructBodySugg,
 };
 
-enum PredicateOrStructBody {
-    Predicate(ast::WherePredicate),
+enum PredicateKindOrStructBody {
+    PredicateKind(ast::WherePredicateKind),
     StructBody(ThinVec<ast::FieldDef>),
 }
 
@@ -218,10 +219,11 @@ impl<'a> Parser<'a> {
                 } else if this.token.can_begin_type() {
                     // Trying to write an associated type bound? (#26271)
                     let snapshot = this.create_snapshot_for_diagnostic();
-                    match this.parse_ty_where_predicate() {
-                        Ok(where_predicate) => {
+                    let lo = this.token.span;
+                    match this.parse_ty_where_predicate_kind() {
+                        Ok(_) => {
                             this.dcx().emit_err(errors::BadAssocTypeBounds {
-                                span: where_predicate.span(),
+                                span: lo.to(this.prev_token.span),
                             });
                             // FIXME - try to continue parsing other generics?
                         }
@@ -340,31 +342,33 @@ impl<'a> Parser<'a> {
         loop {
             let where_sp = where_lo.to(self.prev_token.span);
             let pred_lo = self.token.span;
-            if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
+            let kind = if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
                 let lifetime = self.expect_lifetime();
                 // Bounds starting with a colon are mandatory, but possibly empty.
                 self.expect(&token::Colon)?;
                 let bounds = self.parse_lt_param_bounds();
-                where_clause.predicates.push(ast::WherePredicate::RegionPredicate(
-                    ast::WhereRegionPredicate {
-                        span: pred_lo.to(self.prev_token.span),
-                        lifetime,
-                        bounds,
-                    },
-                ));
+                ast::WherePredicateKind::RegionPredicate(ast::WhereRegionPredicate {
+                    lifetime,
+                    bounds,
+                })
             } else if self.check_type() {
-                match self.parse_ty_where_predicate_or_recover_tuple_struct_body(
+                match self.parse_ty_where_predicate_kind_or_recover_tuple_struct_body(
                     struct_, pred_lo, where_sp,
                 )? {
-                    PredicateOrStructBody::Predicate(pred) => where_clause.predicates.push(pred),
-                    PredicateOrStructBody::StructBody(body) => {
+                    PredicateKindOrStructBody::PredicateKind(kind) => kind,
+                    PredicateKindOrStructBody::StructBody(body) => {
                         tuple_struct_body = Some(body);
                         break;
                     }
                 }
             } else {
                 break;
-            }
+            };
+            where_clause.predicates.push(ast::WherePredicate {
+                kind,
+                id: DUMMY_NODE_ID,
+                span: pred_lo.to(self.prev_token.span),
+            });
 
             let prev_token = self.prev_token.span;
             let ate_comma = self.eat(&token::Comma);
@@ -384,12 +388,12 @@ impl<'a> Parser<'a> {
         Ok((where_clause, tuple_struct_body))
     }
 
-    fn parse_ty_where_predicate_or_recover_tuple_struct_body(
+    fn parse_ty_where_predicate_kind_or_recover_tuple_struct_body(
         &mut self,
         struct_: Option<(Ident, Span)>,
         pred_lo: Span,
         where_sp: Span,
-    ) -> PResult<'a, PredicateOrStructBody> {
+    ) -> PResult<'a, PredicateKindOrStructBody> {
         let mut snapshot = None;
 
         if let Some(struct_) = struct_
@@ -399,8 +403,8 @@ impl<'a> Parser<'a> {
             snapshot = Some((struct_, self.create_snapshot_for_diagnostic()));
         };
 
-        match self.parse_ty_where_predicate() {
-            Ok(pred) => Ok(PredicateOrStructBody::Predicate(pred)),
+        match self.parse_ty_where_predicate_kind() {
+            Ok(pred) => Ok(PredicateKindOrStructBody::PredicateKind(pred)),
             Err(type_err) => {
                 let Some(((struct_name, body_insertion_point), mut snapshot)) = snapshot else {
                     return Err(type_err);
@@ -436,7 +440,7 @@ impl<'a> Parser<'a> {
                         });
 
                         self.restore_snapshot(snapshot);
-                        Ok(PredicateOrStructBody::StructBody(body))
+                        Ok(PredicateKindOrStructBody::StructBody(body))
                     }
                     Ok(_) => Err(type_err),
                     Err(body_err) => {
@@ -448,8 +452,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn parse_ty_where_predicate(&mut self) -> PResult<'a, ast::WherePredicate> {
-        let lo = self.token.span;
+    fn parse_ty_where_predicate_kind(&mut self) -> PResult<'a, ast::WherePredicateKind> {
         // Parse optional `for<'a, 'b>`.
         // This `for` is parsed greedily and applies to the whole predicate,
         // the bounded type can have its own `for` applying only to it.
@@ -464,8 +467,7 @@ impl<'a> Parser<'a> {
         let ty = self.parse_ty_for_where_clause()?;
         if self.eat(&token::Colon) {
             let bounds = self.parse_generic_bounds()?;
-            Ok(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
-                span: lo.to(self.prev_token.span),
+            Ok(ast::WherePredicateKind::BoundPredicate(ast::WhereBoundPredicate {
                 bound_generic_params: lifetime_defs,
                 bounded_ty: ty,
                 bounds,
@@ -474,11 +476,7 @@ impl<'a> Parser<'a> {
         // FIXME: We are just dropping the binders in lifetime_defs on the floor here.
         } else if self.eat(&token::Eq) || self.eat(&token::EqEq) {
             let rhs_ty = self.parse_ty()?;
-            Ok(ast::WherePredicate::EqPredicate(ast::WhereEqPredicate {
-                span: lo.to(self.prev_token.span),
-                lhs_ty: ty,
-                rhs_ty,
-            }))
+            Ok(ast::WherePredicateKind::EqPredicate(ast::WhereEqPredicate { lhs_ty: ty, rhs_ty }))
         } else {
             self.maybe_recover_bounds_doubled_colon(&ty)?;
             self.unexpected_any()
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index c4326427f67..004b5b34813 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -1,11 +1,12 @@
 use rustc_ast::mut_visit::{self, MutVisitor};
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, BinOpToken, Delimiter, IdentIsRaw, Token};
+use rustc_ast::util::parser::AssocOp;
 use rustc_ast::visit::{self, Visitor};
 use rustc_ast::{
-    self as ast, Arm, AttrVec, BinOpKind, BindingMode, ByRef, Expr, ExprKind, ExprPrecedence,
-    LocalKind, MacCall, Mutability, Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd,
-    RangeSyntax, Stmt, StmtKind,
+    self as ast, Arm, AttrVec, BinOpKind, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall,
+    Mutability, Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt,
+    StmtKind,
 };
 use rustc_ast_pretty::pprust;
 use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey};
@@ -458,7 +459,7 @@ impl<'a> Parser<'a> {
                 .create_err(UnexpectedExpressionInPattern {
                     span,
                     is_bound,
-                    expr_precedence: expr.precedence().order(),
+                    expr_precedence: expr.precedence(),
                 })
                 .stash(span, StashKey::ExprInPat)
                 .unwrap(),
@@ -545,7 +546,8 @@ impl<'a> Parser<'a> {
                             let expr = match &err.args["expr_precedence"] {
                                 DiagArgValue::Number(expr_precedence) => {
                                     if *expr_precedence
-                                        <= ExprPrecedence::Binary(BinOpKind::Eq).order() as i32
+                                        <= AssocOp::from_ast_binop(BinOpKind::Eq).precedence()
+                                            as i32
                                     {
                                         format!("({expr})")
                                     } else {
@@ -568,8 +570,9 @@ impl<'a> Parser<'a> {
                                 }
                                 Some(guard) => {
                                     // Are parentheses required around the old guard?
-                                    let wrap_guard = guard.precedence().order()
-                                        <= ExprPrecedence::Binary(BinOpKind::And).order();
+                                    let wrap_guard = guard.precedence()
+                                        <= AssocOp::from_ast_binop(BinOpKind::And).precedence()
+                                            as i8;
 
                                     err.subdiagnostic(
                                         UnexpectedExpressionInPatternSugg::UpdateGuard {
diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs
index db34189be2a..b8f66a2b2ec 100644
--- a/compiler/rustc_passes/src/input_stats.rs
+++ b/compiler/rustc_passes/src/input_stats.rs
@@ -360,11 +360,10 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
     }
 
     fn visit_where_predicate(&mut self, p: &'v hir::WherePredicate<'v>) {
-        record_variants!((self, p, p, None, hir, WherePredicate, WherePredicate), [
-            BoundPredicate,
-            RegionPredicate,
-            EqPredicate
-        ]);
+        record_variants!(
+            (self, p, p.kind, Some(p.hir_id), hir, WherePredicate, WherePredicateKind),
+            [BoundPredicate, RegionPredicate, EqPredicate]
+        );
         hir_visit::walk_where_predicate(self, p)
     }
 
@@ -611,7 +610,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
     }
 
     fn visit_where_predicate(&mut self, p: &'v ast::WherePredicate) {
-        record_variants!((self, p, p, None, ast, WherePredicate, WherePredicate), [
+        record_variants!((self, p, &p.kind, None, ast, WherePredicate, WherePredicateKind), [
             BoundPredicate,
             RegionPredicate,
             EqPredicate
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 235434326aa..60815ffdb1b 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -1268,15 +1268,14 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
         debug!("visit_where_predicate {:?}", p);
         let previous_value = replace(&mut self.diag_metadata.current_where_predicate, Some(p));
         self.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| {
-            if let WherePredicate::BoundPredicate(WhereBoundPredicate {
-                ref bounded_ty,
-                ref bounds,
-                ref bound_generic_params,
-                span: predicate_span,
+            if let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
+                bounded_ty,
+                bounds,
+                bound_generic_params,
                 ..
-            }) = p
+            }) = &p.kind
             {
-                let span = predicate_span.shrink_to_lo().to(bounded_ty.span.shrink_to_lo());
+                let span = p.span.shrink_to_lo().to(bounded_ty.span.shrink_to_lo());
                 this.with_generic_param_rib(
                     bound_generic_params,
                     RibKind::Normal,
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 4a157049964..663c3ac0045 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -1317,21 +1317,24 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
     /// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`.
     fn restrict_assoc_type_in_where_clause(&mut self, span: Span, err: &mut Diag<'_>) -> bool {
         // Detect that we are actually in a `where` predicate.
-        let (bounded_ty, bounds, where_span) =
-            if let Some(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
-                bounded_ty,
-                bound_generic_params,
-                bounds,
-                span,
-            })) = self.diag_metadata.current_where_predicate
-            {
-                if !bound_generic_params.is_empty() {
-                    return false;
-                }
-                (bounded_ty, bounds, span)
-            } else {
+        let (bounded_ty, bounds, where_span) = if let Some(ast::WherePredicate {
+            kind:
+                ast::WherePredicateKind::BoundPredicate(ast::WhereBoundPredicate {
+                    bounded_ty,
+                    bound_generic_params,
+                    bounds,
+                }),
+            span,
+            ..
+        }) = self.diag_metadata.current_where_predicate
+        {
+            if !bound_generic_params.is_empty() {
                 return false;
-            };
+            }
+            (bounded_ty, bounds, span)
+        } else {
+            return false;
+        };
 
         // Confirm that the target is an associated type.
         let (ty, _, path) = if let ast::TyKind::Path(Some(qself), path) = &bounded_ty.kind {
@@ -2840,9 +2843,10 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                         // for<'a, 'b> T: Trait<T> + 'b
                         // ^^^^^^^^^^^  suggest outer binder `for<'a, 'b>`
                         if let LifetimeBinderKind::WhereBound = kind
-                            && let Some(ast::WherePredicate::BoundPredicate(
+                            && let Some(predicate) = self.diag_metadata.current_where_predicate
+                            && let ast::WherePredicateKind::BoundPredicate(
                                 ast::WhereBoundPredicate { bounded_ty, bounds, .. },
-                            )) = self.diag_metadata.current_where_predicate
+                            ) = &predicate.kind
                             && bounded_ty.id == binder
                         {
                             for bound in bounds {
@@ -3473,7 +3477,6 @@ fn mk_where_bound_predicate(
     };
 
     let new_where_bound_predicate = ast::WhereBoundPredicate {
-        span: DUMMY_SP,
         bound_generic_params: ThinVec::new(),
         bounded_ty: ast::ptr::P(ty.clone()),
         bounds: vec![ast::GenericBound::Trait(ast::PolyTraitRef {
diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl
index 893c532f1fb..8fd87893a98 100644
--- a/compiler/rustc_session/messages.ftl
+++ b/compiler/rustc_session/messages.ftl
@@ -84,8 +84,6 @@ session_not_supported = not supported
 
 session_octal_float_literal_not_supported = octal float literal is not supported
 
-session_optimization_fuel_exhausted = optimization-fuel-exhausted: {$msg}
-
 session_profile_sample_use_file_does_not_exist = file `{$path}` passed to `-C profile-sample-use` does not exist
 
 session_profile_use_file_does_not_exist = file `{$path}` passed to `-C profile-use` does not exist
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index d60c56fee75..0124397ea46 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -2356,14 +2356,6 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
         early_dcx.early_warn(format!("number of threads was capped at {}", parse::MAX_THREADS_CAP));
     }
 
-    let fuel = unstable_opts.fuel.is_some() || unstable_opts.print_fuel.is_some();
-    if fuel && unstable_opts.threads > 1 {
-        early_dcx.early_fatal("optimization fuel is incompatible with multiple threads");
-    }
-    if fuel && cg.incremental.is_some() {
-        early_dcx.early_fatal("optimization fuel is incompatible with incremental compilation");
-    }
-
     let incremental = cg.incremental.as_ref().map(PathBuf::from);
 
     let assert_incr_state = parse_assert_incr_state(early_dcx, &unstable_opts.assert_incr_state);
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index 33f84f10447..736a5ce0704 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -464,12 +464,6 @@ pub fn report_lit_error(
 }
 
 #[derive(Diagnostic)]
-#[diag(session_optimization_fuel_exhausted)]
-pub(crate) struct OptimisationFuelExhausted {
-    pub(crate) msg: String,
-}
-
-#[derive(Diagnostic)]
 #[diag(session_incompatible_linker_flavor)]
 #[note]
 pub(crate) struct IncompatibleLinkerFlavor {
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index edee7b4468c..a2d75917c82 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -394,7 +394,6 @@ mod desc {
     pub(crate) const parse_collapse_macro_debuginfo: &str = "one of `no`, `external`, or `yes`";
     pub(crate) const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`";
     pub(crate) const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of();
-    pub(crate) const parse_optimization_fuel: &str = "crate=integer";
     pub(crate) const parse_dump_mono_stats: &str = "`markdown` (default) or `json`";
     pub(crate) const parse_instrument_coverage: &str = parse_bool;
     pub(crate) const parse_coverage_options: &str =
@@ -948,21 +947,6 @@ pub mod parse {
         true
     }
 
-    pub(crate) fn parse_optimization_fuel(
-        slot: &mut Option<(String, u64)>,
-        v: Option<&str>,
-    ) -> bool {
-        match v {
-            None => false,
-            Some(s) => {
-                let [crate_name, fuel] = *s.split('=').collect::<Vec<_>>() else { return false };
-                let Ok(fuel) = fuel.parse::<u64>() else { return false };
-                *slot = Some((crate_name.to_string(), fuel));
-                true
-            }
-        }
-    }
-
     pub(crate) fn parse_unpretty(slot: &mut Option<String>, v: Option<&str>) -> bool {
         match v {
             None => false,
@@ -1794,8 +1778,6 @@ options! {
         `shallow` prints only type names, `none` prints nothing and disables `{:?}`. (default: `full`)"),
     force_unstable_if_unmarked: bool = (false, parse_bool, [TRACKED],
         "force all crates to be `rustc_private` unstable (default: no)"),
-    fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED],
-        "set the optimization fuel quota for a crate"),
     function_return: FunctionReturn = (FunctionReturn::default(), parse_function_return, [TRACKED],
         "replace returns with jumps to `__x86_return_thunk` (default: `keep`)"),
     function_sections: Option<bool> = (None, parse_opt_bool, [TRACKED],
@@ -1978,8 +1960,6 @@ options! {
     #[rustc_lint_opt_deny_field_access("use `Session::print_codegen_stats` instead of this field")]
     print_codegen_stats: bool = (false, parse_bool, [UNTRACKED],
         "print codegen statistics (default: no)"),
-    print_fuel: Option<String> = (None, parse_opt_string, [TRACKED],
-        "make rustc print the total optimization fuel used by a crate"),
     print_llvm_passes: bool = (false, parse_bool, [UNTRACKED],
         "print the LLVM optimization passes being run (default: no)"),
     print_mono_items: Option<String> = (None, parse_opt_string, [UNTRACKED],
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 29fabdd1deb..f585410adb9 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -4,7 +4,6 @@ use std::path::{Path, PathBuf};
 use std::str::FromStr;
 use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
-use std::sync::atomic::Ordering::SeqCst;
 use std::{env, fmt, io};
 
 use rustc_data_structures::flock;
@@ -12,7 +11,7 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
 use rustc_data_structures::jobserver::{self, Client};
 use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef};
 use rustc_data_structures::sync::{
-    AtomicU64, DynSend, DynSync, Lock, Lrc, MappedReadGuard, ReadGuard, RwLock,
+    DynSend, DynSync, Lock, Lrc, MappedReadGuard, ReadGuard, RwLock,
 };
 use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter;
 use rustc_errors::codes::*;
@@ -49,13 +48,6 @@ use crate::parse::{ParseSess, add_feature_diagnostics};
 use crate::search_paths::SearchPath;
 use crate::{errors, filesearch, lint};
 
-struct OptimizationFuel {
-    /// If `-zfuel=crate=n` is specified, initially set to `n`, otherwise `0`.
-    remaining: u64,
-    /// We're rejecting all further optimizations.
-    out_of_fuel: bool,
-}
-
 /// The behavior of the CTFE engine when an error occurs with regards to backtraces.
 #[derive(Clone, Copy)]
 pub enum CtfeBacktrace {
@@ -163,12 +155,6 @@ pub struct Session {
     /// Data about code being compiled, gathered during compilation.
     pub code_stats: CodeStats,
 
-    /// Tracks fuel info if `-zfuel=crate=n` is specified.
-    optimization_fuel: Lock<OptimizationFuel>,
-
-    /// Always set to zero and incremented so that we can print fuel expended by a crate.
-    pub print_fuel: AtomicU64,
-
     /// Loaded up early on in the initialization of this `Session` to avoid
     /// false positives about a job server in our environment.
     pub jobserver: Client,
@@ -532,41 +518,6 @@ impl Session {
         self.opts.incremental.as_ref().map(|_| self.incr_comp_session_dir())
     }
 
-    /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n.
-    /// This expends fuel if applicable, and records fuel if applicable.
-    pub fn consider_optimizing(
-        &self,
-        get_crate_name: impl Fn() -> Symbol,
-        msg: impl Fn() -> String,
-    ) -> bool {
-        let mut ret = true;
-        if let Some((ref c, _)) = self.opts.unstable_opts.fuel {
-            if c == get_crate_name().as_str() {
-                assert_eq!(self.threads(), 1);
-                let mut fuel = self.optimization_fuel.lock();
-                ret = fuel.remaining != 0;
-                if fuel.remaining == 0 && !fuel.out_of_fuel {
-                    if self.dcx().can_emit_warnings() {
-                        // We only call `msg` in case we can actually emit warnings.
-                        // Otherwise, this could cause a `must_produce_diag` ICE
-                        // (issue #79546).
-                        self.dcx().emit_warn(errors::OptimisationFuelExhausted { msg: msg() });
-                    }
-                    fuel.out_of_fuel = true;
-                } else if fuel.remaining > 0 {
-                    fuel.remaining -= 1;
-                }
-            }
-        }
-        if let Some(ref c) = self.opts.unstable_opts.print_fuel {
-            if c == get_crate_name().as_str() {
-                assert_eq!(self.threads(), 1);
-                self.print_fuel.fetch_add(1, SeqCst);
-            }
-        }
-        ret
-    }
-
     /// Is this edition 2015?
     pub fn is_rust_2015(&self) -> bool {
         self.edition().is_rust_2015()
@@ -1097,12 +1048,6 @@ pub fn build_session(
         Lrc::new(SearchPath::from_sysroot_and_triple(&sysroot, target_triple))
     };
 
-    let optimization_fuel = Lock::new(OptimizationFuel {
-        remaining: sopts.unstable_opts.fuel.as_ref().map_or(0, |&(_, i)| i),
-        out_of_fuel: false,
-    });
-    let print_fuel = AtomicU64::new(0);
-
     let prof = SelfProfilerRef::new(
         self_profiler,
         sopts.unstable_opts.time_passes.then(|| sopts.unstable_opts.time_passes_format),
@@ -1130,8 +1075,6 @@ pub fn build_session(
         incr_comp_session: RwLock::new(IncrCompSession::NotInitialized),
         prof,
         code_stats: Default::default(),
-        optimization_fuel,
-        print_fuel,
         jobserver: jobserver::client(),
         lint_store: None,
         registered_lints: false,
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 46e245fb71f..5252c446e1d 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1728,7 +1728,6 @@ symbols! {
         rustc_partition_reused,
         rustc_pass_by_value,
         rustc_peek,
-        rustc_peek_definite_init,
         rustc_peek_liveness,
         rustc_peek_maybe_init,
         rustc_peek_maybe_uninit,
diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs
index bd47d12ef9f..3225cdc759d 100644
--- a/compiler/rustc_target/src/spec/tests/tests_impl.rs
+++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs
@@ -1,5 +1,7 @@
 use std::assert_matches::assert_matches;
 
+use rustc_data_structures::fx::FxHashSet;
+
 use super::super::*;
 
 // Test target self-consistency and JSON encoding/decoding roundtrip.
@@ -19,6 +21,9 @@ impl Target {
         if self.is_like_msvc {
             assert!(self.is_like_windows);
         }
+        if self.os == "emscripten" {
+            assert!(self.is_like_wasm);
+        }
 
         // Check that default linker flavor is compatible with some other key properties.
         assert_eq!(self.is_like_osx, matches!(self.linker_flavor, LinkerFlavor::Darwin(..)));
@@ -137,7 +142,7 @@ impl Target {
             assert!(self.dynamic_linking);
         }
         // Apparently PIC was slow on wasm at some point, see comments in wasm_base.rs
-        if self.dynamic_linking && !(self.is_like_wasm && self.os != "emscripten") {
+        if self.dynamic_linking && !self.is_like_wasm {
             assert_eq!(self.relocation_model, RelocModel::Pic);
         }
         if self.position_independent_executables {
@@ -170,6 +175,27 @@ impl Target {
             }
             _ => {}
         }
+
+        // Check that the given target-features string makes some basic sense.
+        if !self.features.is_empty() {
+            let mut features_enabled = FxHashSet::default();
+            let mut features_disabled = FxHashSet::default();
+            for feat in self.features.split(',') {
+                if let Some(feat) = feat.strip_prefix("+") {
+                    features_enabled.insert(feat);
+                    if features_disabled.contains(feat) {
+                        panic!("target feature `{feat}` is both enabled and disabled");
+                    }
+                } else if let Some(feat) = feat.strip_prefix("-") {
+                    features_disabled.insert(feat);
+                    if features_enabled.contains(feat) {
+                        panic!("target feature `{feat}` is both enabled and disabled");
+                    }
+                } else {
+                    panic!("target feature `{feat}` is invalid, must start with `+` or `-`");
+                }
+            }
+        }
     }
 
     // Add your target to the whitelist if it has `std` library
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
index c7ad14ac0bf..0f9d4cb1982 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
@@ -1792,12 +1792,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
     fn suggest_specify_actual_length(
         &self,
-        terr: TypeError<'_>,
-        trace: &TypeTrace<'_>,
+        terr: TypeError<'tcx>,
+        trace: &TypeTrace<'tcx>,
         span: Span,
     ) -> Option<TypeErrorAdditionalDiags> {
         let hir = self.tcx.hir();
-        let TypeError::FixedArraySize(sz) = terr else {
+        let TypeError::ArraySize(sz) = terr else {
             return None;
         };
         let tykind = match self.tcx.hir_node_by_def_id(trace.cause.body_id) {
@@ -1838,9 +1838,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         if let Some(tykind) = tykind
             && let hir::TyKind::Array(_, length) = tykind
             && let hir::ArrayLen::Body(ct) = length
+            && let Some((scalar, ty)) = sz.found.try_to_scalar()
+            && ty == self.tcx.types.usize
         {
             let span = ct.span();
-            Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength { span, length: sz.found })
+            Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength {
+                span,
+                length: scalar.to_target_usize(&self.tcx).unwrap(),
+            })
         } else {
             None
         }
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 107ebe8adf0..75191894525 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
@@ -1803,24 +1803,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 StringPart::highlighted("cargo tree".to_string()),
                 StringPart::normal("` to explore your dependency tree".to_string()),
             ]);
-
-            // FIXME: this is a giant hack for the benefit of this specific diagnostic. Because
-            // we're so nested in method calls before the error gets emitted, bubbling a single bit
-            // flag informing the top level caller to stop adding extra detail to the diagnostic,
-            // would actually be harder to follow. So we do something naughty here: we consume the
-            // diagnostic, emit it and leave in its place a "delayed bug" that will continue being
-            // modified but won't actually be printed to end users. This *is not ideal*, but allows
-            // us to reduce the verbosity of an error that is already quite verbose and increase its
-            // specificity. Below we modify the main message as well, in a way that *could* break if
-            // the implementation of Diagnostics change significantly, but that would be caught with
-            // a make test failure when this diagnostic is tested.
-            err.primary_message(format!(
-                "{} because the trait comes from a different crate version",
-                err.messages[0].0.as_str().unwrap(),
-            ));
-            let diag = err.clone();
-            err.downgrade_to_delayed_bug();
-            self.tcx.dcx().emit_diagnostic(diag);
             return true;
         }
 
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 5ad15feadff..27b45f70946 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -5312,9 +5312,10 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>(
     };
     let name = tcx.item_name(proj.projection_term.def_id);
     let mut predicates = generics.predicates.iter().peekable();
-    let mut prev: Option<&hir::WhereBoundPredicate<'_>> = None;
+    let mut prev: Option<(&hir::WhereBoundPredicate<'_>, Span)> = None;
     while let Some(pred) = predicates.next() {
-        let hir::WherePredicate::BoundPredicate(pred) = pred else {
+        let curr_span = pred.span;
+        let hir::WherePredicateKind::BoundPredicate(pred) = pred.kind else {
             continue;
         };
         let mut bounds = pred.bounds.iter();
@@ -5340,8 +5341,8 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>(
                         .iter()
                         .filter(|p| {
                             matches!(
-                                p,
-                                hir::WherePredicate::BoundPredicate(p)
+                                p.kind,
+                                hir::WherePredicateKind::BoundPredicate(p)
                                 if hir::PredicateOrigin::WhereClause == p.origin
                             )
                         })
@@ -5351,20 +5352,21 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>(
                     // There's only one `where` bound, that needs to be removed. Remove the whole
                     // `where` clause.
                     generics.where_clause_span
-                } else if let Some(hir::WherePredicate::BoundPredicate(next)) = predicates.peek()
+                } else if let Some(next_pred) = predicates.peek()
+                    && let hir::WherePredicateKind::BoundPredicate(next) = next_pred.kind
                     && pred.origin == next.origin
                 {
                     // There's another bound, include the comma for the current one.
-                    pred.span.until(next.span)
-                } else if let Some(prev) = prev
+                    curr_span.until(next_pred.span)
+                } else if let Some((prev, prev_span)) = prev
                     && pred.origin == prev.origin
                 {
                     // Last bound, try to remove the previous comma.
-                    prev.span.shrink_to_hi().to(pred.span)
+                    prev_span.shrink_to_hi().to(curr_span)
                 } else if pred.origin == hir::PredicateOrigin::WhereClause {
-                    pred.span.with_hi(generics.where_clause_span.hi())
+                    curr_span.with_hi(generics.where_clause_span.hi())
                 } else {
-                    pred.span
+                    curr_span
                 };
 
                 err.span_suggestion_verbose(
@@ -5417,7 +5419,7 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>(
                 );
             }
         }
-        prev = Some(pred);
+        prev = Some((pred, curr_span));
     }
 }
 
diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
index 78e92e60b23..e0a9ddf1876 100644
--- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
+++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
@@ -150,8 +150,8 @@ fn get_sized_bounds(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]>
                     .predicates
                     .iter()
                     .filter_map(|pred| {
-                        match pred {
-                            hir::WherePredicate::BoundPredicate(pred)
+                        match pred.kind {
+                            hir::WherePredicateKind::BoundPredicate(pred)
                                 if pred.bounded_ty.hir_id.owner.to_def_id() == trait_def_id =>
                             {
                                 // Fetch spans for trait bounds that are Sized:
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 7d6c38c11f3..069fab6a6e6 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -551,8 +551,18 @@ pub fn try_evaluate_const<'tcx>(
         | ty::ConstKind::Placeholder(_)
         | ty::ConstKind::Expr(_) => Err(EvaluateConstErr::HasGenericsOrInfers),
         ty::ConstKind::Unevaluated(uv) => {
-            // Postpone evaluation of constants that depend on generic parameters or inference variables.
-            let (args, param_env) = if tcx.features().generic_const_exprs()
+            // Postpone evaluation of constants that depend on generic parameters or
+            // inference variables.
+            //
+            // We use `TypingMode::PostAnalysis`  here which is 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).
+            // As a result we always use a revealed env when resolving the instance to evaluate.
+            //
+            // FIXME: `const_eval_resolve_for_typeck` should probably just modify the env itself
+            // instead of having this logic here
+            let (args, typing_env) = if tcx.features().generic_const_exprs()
                 && uv.has_non_region_infer()
             {
                 // `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause
@@ -568,13 +578,17 @@ pub fn try_evaluate_const<'tcx>(
                             // the generic arguments provided for it, then we should *not* attempt to evaluate it.
                             return Err(EvaluateConstErr::HasGenericsOrInfers);
                         } else {
-                            (replace_param_and_infer_args_with_placeholder(tcx, uv.args), param_env)
+                            let args = replace_param_and_infer_args_with_placeholder(tcx, uv.args);
+                            let typing_env = infcx
+                                .typing_env(tcx.erase_regions(param_env))
+                                .with_post_analysis_normalized(tcx);
+                            (args, typing_env)
                         }
                     }
                     Err(_) | Ok(None) => {
                         let args = GenericArgs::identity_for_item(tcx, uv.def);
-                        let param_env = tcx.param_env(uv.def);
-                        (args, param_env)
+                        let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def);
+                        (args, typing_env)
                     }
                 }
             } else if tcx.def_kind(uv.def) == DefKind::AnonConst && uv.has_non_region_infer() {
@@ -593,27 +607,20 @@ pub fn try_evaluate_const<'tcx>(
                 );
 
                 let args = GenericArgs::identity_for_item(tcx, uv.def);
-                let param_env = tcx.param_env(uv.def);
-                (args, param_env)
+                let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def);
+                (args, typing_env)
             } else {
                 // FIXME: This codepath is reachable under `associated_const_equality` and in the
                 // future will be reachable by `min_generic_const_args`. We should handle inference
                 // variables and generic parameters properly instead of doing nothing.
-                (uv.args, param_env)
+                let typing_env = infcx
+                    .typing_env(tcx.erase_regions(param_env))
+                    .with_post_analysis_normalized(tcx);
+                (uv.args, typing_env)
             };
             let uv = ty::UnevaluatedConst::new(uv.def, args);
 
-            // 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).
-            // As a result we always use a revealed env when resolving the instance to evaluate.
-            //
-            // FIXME: `const_eval_resolve_for_typeck` should probably just modify the env itself
-            // instead of having this logic here
-            let typing_env =
-                tcx.erase_regions(infcx.typing_env(param_env)).with_post_analysis_normalized(tcx);
             let erased_uv = tcx.erase_regions(uv);
-
             use rustc_middle::mir::interpret::ErrorHandled;
             match tcx.const_eval_resolve_for_typeck(typing_env, erased_uv, DUMMY_SP) {
                 Ok(Ok(val)) => Ok(ty::Const::new_value(
diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs
index 59dea769511..55671b84dbc 100644
--- a/compiler/rustc_type_ir/src/error.rs
+++ b/compiler/rustc_type_ir/src/error.rs
@@ -29,7 +29,7 @@ pub enum TypeError<I: Interner> {
     Mutability,
     ArgumentMutability(usize),
     TupleSize(ExpectedFound<usize>),
-    FixedArraySize(ExpectedFound<u64>),
+    ArraySize(ExpectedFound<I::Const>),
     ArgCount,
 
     RegionsDoesNotOutlive(I::Region, I::Region),
@@ -69,7 +69,7 @@ impl<I: Interner> TypeError<I> {
         use self::TypeError::*;
         match self {
             CyclicTy(_) | CyclicConst(_) | SafetyMismatch(_) | PolarityMismatch(_) | Mismatch
-            | AbiMismatch(_) | FixedArraySize(_) | ArgumentSorts(..) | Sorts(_)
+            | AbiMismatch(_) | ArraySize(_) | ArgumentSorts(..) | Sorts(_)
             | VariadicMismatch(_) | TargetFeatureCast(_) => false,
 
             Mutability
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index 3793d2c5241..a201f2b1c11 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -257,8 +257,6 @@ pub trait Const<I: Interner<Const = Self>>:
     + Relate<I>
     + Flags
 {
-    fn try_to_target_usize(self, interner: I) -> Option<u64>;
-
     fn new_infer(interner: I, var: ty::InferConst) -> Self;
 
     fn new_var(interner: I, var: ty::ConstVid) -> Self;
diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs
index 6b301b16060..0b013b2017f 100644
--- a/compiler/rustc_type_ir/src/relate.rs
+++ b/compiler/rustc_type_ir/src/relate.rs
@@ -501,19 +501,10 @@ pub fn structurally_relate_tys<I: Interner, R: TypeRelation<I>>(
             let t = relation.relate(a_t, b_t)?;
             match relation.relate(sz_a, sz_b) {
                 Ok(sz) => Ok(Ty::new_array_with_const_len(cx, t, sz)),
-                Err(err) => {
-                    // Check whether the lengths are both concrete/known values,
-                    // but are unequal, for better diagnostics.
-                    let sz_a = sz_a.try_to_target_usize(cx);
-                    let sz_b = sz_b.try_to_target_usize(cx);
-
-                    match (sz_a, sz_b) {
-                        (Some(sz_a_val), Some(sz_b_val)) if sz_a_val != sz_b_val => {
-                            Err(TypeError::FixedArraySize(ExpectedFound::new(sz_a_val, sz_b_val)))
-                        }
-                        _ => Err(err),
-                    }
+                Err(TypeError::ConstMismatch(_)) => {
+                    Err(TypeError::ArraySize(ExpectedFound::new(sz_a, sz_b)))
                 }
+                Err(e) => Err(e),
             }
         }