diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl
index 1b91c33742d8b..f29c98164d028 100644
--- a/compiler/rustc_ast_lowering/messages.ftl
+++ b/compiler/rustc_ast_lowering/messages.ftl
@@ -88,7 +88,7 @@ ast_lowering_invalid_abi_clobber_abi =
     invalid ABI for `clobber_abi`
     .note = the following ABIs are supported on this target: {$supported_abis}
 
-ast_lowering_invalid_abi_suggestion = did you mean
+ast_lowering_invalid_abi_suggestion = there's a similarly named valid ABI `{$suggestion}`
 
 ast_lowering_invalid_asm_template_modifier_const =
     asm template modifiers are not allowed for `const` arguments
diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs
index cfd32fc066f41..87af7959a884d 100644
--- a/compiler/rustc_ast_lowering/src/asm.rs
+++ b/compiler/rustc_ast_lowering/src/asm.rs
@@ -195,7 +195,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         }
                     }
                     InlineAsmOperand::Const { anon_const } => hir::InlineAsmOperand::Const {
-                        anon_const: self.lower_anon_const_to_anon_const(anon_const),
+                        anon_const: self.lower_const_block(anon_const),
                     },
                     InlineAsmOperand::Sym { sym } => {
                         let static_def_id = self
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index f31e2db051d81..766c02334087f 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -46,8 +46,9 @@ pub(crate) struct TupleStructWithDefault {
 #[derive(Subdiagnostic)]
 #[suggestion(
     ast_lowering_invalid_abi_suggestion,
-    code = "{suggestion}",
-    applicability = "maybe-incorrect"
+    code = "\"{suggestion}\"",
+    applicability = "maybe-incorrect",
+    style = "verbose"
 )]
 pub(crate) struct InvalidAbiSuggestion {
     #[primary_span]
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index efbd1711daa92..9c3db7abc1ccc 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -671,10 +671,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let span = self.lower_span(arm.span);
         self.lower_attrs(hir_id, &arm.attrs, arm.span);
         let is_never_pattern = pat.is_never_pattern();
-        let body = if let Some(body) = &arm.body
+        // We need to lower the body even if it's unneeded for never pattern in match,
+        // ensure that we can get HirId for DefId if need (issue #137708).
+        let body = arm.body.as_ref().map(|x| self.lower_expr(x));
+        let body = if let Some(body) = body
             && !is_never_pattern
         {
-            self.lower_expr(body)
+            body
         } else {
             // Either `body.is_none()` or `is_never_pattern` here.
             if !is_never_pattern {
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index a4fc4b3e3a121..15802101f71a5 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -1510,7 +1510,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             span: abi.span,
             suggestion: suggested_name.map(|suggested_name| InvalidAbiSuggestion {
                 span: abi.span,
-                suggestion: format!("\"{suggested_name}\""),
+                suggestion: suggested_name.to_string(),
             }),
             command: "rustc --print=calling-conventions".to_string(),
         });
diff --git a/compiler/rustc_error_codes/src/error_codes/E0133.md b/compiler/rustc_error_codes/src/error_codes/E0133.md
index 8ca3f03ce156f..854cca3d10ff2 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0133.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0133.md
@@ -45,6 +45,7 @@ unsafe fn g() {
 ```
 
 Linting against this is controlled via the `unsafe_op_in_unsafe_fn` lint, which
-is `allow` by default but will be upgraded to `warn` in a future edition.
+is `warn` by default in the 2024 edition and `allow` by default in earlier
+editions.
 
 [unsafe-section]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 3c75bc588a138..4735c28bfa9ab 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -3506,7 +3506,7 @@ pub enum InlineAsmOperand<'hir> {
         out_expr: Option<&'hir Expr<'hir>>,
     },
     Const {
-        anon_const: &'hir AnonConst,
+        anon_const: ConstBlock,
     },
     SymFn {
         expr: &'hir Expr<'hir>,
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index d5fa7ec366b28..e349e23f7dcf5 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -1447,7 +1447,7 @@ pub fn walk_inline_asm<'v, V: Visitor<'v>>(
                 visit_opt!(visitor, visit_expr, out_expr);
             }
             InlineAsmOperand::Const { anon_const, .. } => {
-                try_visit!(visitor.visit_anon_const(anon_const));
+                try_visit!(visitor.visit_inline_const(anon_const));
             }
             InlineAsmOperand::SymFn { expr, .. } => {
                 try_visit!(visitor.visit_expr(expr));
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
index 511947404506c..590ade516ec64 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
@@ -1,5 +1,3 @@
-use std::assert_matches::debug_assert_matches;
-
 use rustc_abi::FieldIdx;
 use rustc_ast::InlineAsmTemplatePiece;
 use rustc_data_structures::fx::FxIndexSet;
@@ -21,6 +19,7 @@ pub struct InlineAsmCtxt<'a, 'tcx: 'a> {
     typing_env: ty::TypingEnv<'tcx>,
     target_features: &'tcx FxIndexSet<Symbol>,
     expr_ty: Box<dyn Fn(&hir::Expr<'tcx>) -> Ty<'tcx> + 'a>,
+    node_ty: Box<dyn Fn(hir::HirId) -> Ty<'tcx> + 'a>,
 }
 
 enum NonAsmTypeReason<'tcx> {
@@ -35,13 +34,15 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
         tcx: TyCtxt<'tcx>,
         def_id: LocalDefId,
         typing_env: ty::TypingEnv<'tcx>,
-        get_operand_ty: impl Fn(&hir::Expr<'tcx>) -> Ty<'tcx> + 'a,
+        expr_ty: impl Fn(&hir::Expr<'tcx>) -> Ty<'tcx> + 'a,
+        node_ty: impl Fn(hir::HirId) -> Ty<'tcx> + 'a,
     ) -> Self {
         InlineAsmCtxt {
             tcx,
             typing_env,
             target_features: tcx.asm_target_features(def_id),
-            expr_ty: Box::new(get_operand_ty),
+            expr_ty: Box::new(expr_ty),
+            node_ty: Box::new(node_ty),
         }
     }
 
@@ -49,6 +50,10 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
         (self.expr_ty)(expr)
     }
 
+    fn node_ty(&self, hir_id: hir::HirId) -> Ty<'tcx> {
+        (self.node_ty)(hir_id)
+    }
+
     // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()`
     fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
         // Type still may have region variables, but `Sized` does not depend
@@ -487,12 +492,23 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                         );
                     }
                 }
-                // Typeck has checked that Const operands are integers.
                 hir::InlineAsmOperand::Const { anon_const } => {
-                    debug_assert_matches!(
-                        self.tcx.type_of(anon_const.def_id).instantiate_identity().kind(),
-                        ty::Error(_) | ty::Int(_) | ty::Uint(_)
-                    );
+                    let ty = self.node_ty(anon_const.hir_id);
+                    match ty.kind() {
+                        ty::Error(_) => {}
+                        _ if ty.is_integral() => {}
+                        _ => {
+                            self.tcx
+                                .dcx()
+                                .struct_span_err(op_sp, "invalid type for `const` operand")
+                                .with_span_label(
+                                    self.tcx.def_span(anon_const.def_id),
+                                    format!("is {} `{}`", ty.kind().article(), ty),
+                                )
+                                .with_help("`const` operands must be of an integer type")
+                                .emit();
+                        }
+                    }
                 }
                 // Typeck has checked that SymFn refers to a function.
                 hir::InlineAsmOperand::SymFn { expr } => {
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index 2cdd9a3a9348a..af1338e50d007 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -186,17 +186,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
                     {
                         Some(parent_did)
                     }
-                    // Exclude `GlobalAsm` here which cannot have generics.
-                    Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. })
-                        if asm.operands.iter().any(|(op, _op_sp)| match op {
-                            hir::InlineAsmOperand::Const { anon_const } => {
-                                anon_const.hir_id == hir_id
-                            }
-                            _ => false,
-                        }) =>
-                    {
-                        Some(parent_did)
-                    }
                     Node::TyPat(_) => Some(parent_did),
                     _ => None,
                 }
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index d564dc9699a88..6936544838c81 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -8,7 +8,7 @@ use rustc_middle::query::plumbing::CyclePlaceholder;
 use rustc_middle::ty::fold::fold_regions;
 use rustc_middle::ty::print::with_forced_trimmed_paths;
 use rustc_middle::ty::util::IntTypeExt;
-use rustc_middle::ty::{self, Article, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
 use rustc_middle::{bug, span_bug};
 use rustc_span::{DUMMY_SP, Ident, Span};
 
@@ -35,13 +35,6 @@ fn anon_const_type_of<'tcx>(icx: &ItemCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx
     let parent_node_id = tcx.parent_hir_id(hir_id);
     let parent_node = tcx.hir_node(parent_node_id);
 
-    let find_const = |&(op, op_sp)| match op {
-        hir::InlineAsmOperand::Const { anon_const } if anon_const.hir_id == hir_id => {
-            Some((anon_const, op_sp))
-        }
-        _ => None,
-    };
-
     match parent_node {
         // Anon consts "inside" the type system.
         Node::ConstArg(&ConstArg {
@@ -50,31 +43,6 @@ fn anon_const_type_of<'tcx>(icx: &ItemCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx
             ..
         }) if anon_hir_id == hir_id => const_arg_anon_type_of(icx, arg_hir_id, span),
 
-        // Anon consts outside the type system.
-        Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. })
-        | Node::Item(&Item { kind: ItemKind::GlobalAsm { asm, .. }, .. })
-            if let Some((anon_const, op_sp)) = asm.operands.iter().find_map(find_const) =>
-        {
-            let ty = tcx.typeck(def_id).node_type(hir_id);
-
-            match ty.kind() {
-                ty::Error(_) => ty,
-                ty::Int(_) | ty::Uint(_) => ty,
-                _ => {
-                    let guar = tcx
-                        .dcx()
-                        .struct_span_err(op_sp, "invalid type for `const` operand")
-                        .with_span_label(
-                            tcx.def_span(anon_const.def_id),
-                            format!("is {} `{}`", ty.kind().article(), ty),
-                        )
-                        .with_help("`const` operands must be of an integer type")
-                        .emit();
-
-                    Ty::new_error(tcx, guar)
-                }
-            }
-        }
         Node::Variant(Variant { disr_expr: Some(e), .. }) if e.hir_id == hir_id => {
             tcx.adt_def(tcx.hir_get_parent_item(hir_id)).repr().discr_type().to_ty(tcx)
         }
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index b3377b487694b..e06398cf3c418 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -1413,7 +1413,8 @@ impl<'a> State<'a> {
                 hir::InlineAsmOperand::Const { ref anon_const } => {
                     s.word("const");
                     s.space();
-                    s.print_anon_const(anon_const);
+                    // Not using `print_inline_const` to avoid additional `const { ... }`
+                    s.ann.nested(s, Nested::Body(anon_const.body))
                 }
                 hir::InlineAsmOperand::SymFn { ref expr } => {
                     s.word("sym_fn");
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 43dfec0f408b5..dec1779d92ca4 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -1853,12 +1853,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return Ty::new_error(tcx, guar);
         }
 
+        // We defer checking whether the element type is `Copy` as it is possible to have
+        // an inference variable as a repeat count and it seems unlikely that `Copy` would
+        // have inference side effects required for type checking to succeed.
+        if tcx.features().generic_arg_infer() {
+            self.deferred_repeat_expr_checks.borrow_mut().push((element, element_ty, count));
         // If the length is 0, we don't create any elements, so we don't copy any.
         // If the length is 1, we don't copy that one element, we move it. Only check
         // for `Copy` if the length is larger, or unevaluated.
-        // FIXME(min_const_generic_exprs): We could perhaps defer this check so that
-        // we don't require `<?0t as Tr>::CONST` doesn't unnecessarily require `Copy`.
-        if count.try_to_target_usize(tcx).is_none_or(|x| x > 1) {
+        } else if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) {
             self.enforce_repeat_element_needs_copy_bound(element, element_ty);
         }
 
@@ -1868,7 +1871,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 
     /// Requires that `element_ty` is `Copy` (unless it's a const expression itself).
-    fn enforce_repeat_element_needs_copy_bound(
+    pub(super) fn enforce_repeat_element_needs_copy_bound(
         &self,
         element: &hir::Expr<'_>,
         element_ty: Ty<'tcx>,
@@ -3771,13 +3774,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         self.check_expr_asm_operand(out_expr, false);
                     }
                 }
+                hir::InlineAsmOperand::Const { ref anon_const } => {
+                    self.check_expr_const_block(anon_const, Expectation::NoExpectation);
+                }
                 hir::InlineAsmOperand::SymFn { expr } => {
                     self.check_expr(expr);
                 }
-                // `AnonConst`s have their own body and is type-checked separately.
-                // As they don't flow into the type system we don't need them to
-                // be well-formed.
-                hir::InlineAsmOperand::Const { .. } => {}
                 hir::InlineAsmOperand::SymStatic { .. } => {}
                 hir::InlineAsmOperand::Label { block } => {
                     let previous_diverges = self.diverges.get();
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 2d7d80e39bc31..c9288b6a9d1e9 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -85,25 +85,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         })
     }
 
-    /// Resolves type and const variables in `ty` if possible. Unlike the infcx
+    /// Resolves type and const variables in `t` if possible. Unlike the infcx
     /// version (resolve_vars_if_possible), this version will
     /// also select obligations if it seems useful, in an effort
     /// to get more type information.
     // FIXME(-Znext-solver): A lot of the calls to this method should
     // probably be `try_structurally_resolve_type` or `structurally_resolve_type` instead.
     #[instrument(skip(self), level = "debug", ret)]
-    pub(crate) fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
+    pub(crate) fn resolve_vars_with_obligations<T: TypeFoldable<TyCtxt<'tcx>>>(
+        &self,
+        mut t: T,
+    ) -> T {
         // No Infer()? Nothing needs doing.
-        if !ty.has_non_region_infer() {
+        if !t.has_non_region_infer() {
             debug!("no inference var, nothing needs doing");
-            return ty;
+            return t;
         }
 
-        // If `ty` is a type variable, see whether we already know what it is.
-        ty = self.resolve_vars_if_possible(ty);
-        if !ty.has_non_region_infer() {
-            debug!(?ty);
-            return ty;
+        // If `t` is a type variable, see whether we already know what it is.
+        t = self.resolve_vars_if_possible(t);
+        if !t.has_non_region_infer() {
+            debug!(?t);
+            return t;
         }
 
         // If not, try resolving pending obligations as much as
@@ -111,7 +114,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // indirect dependencies that don't seem worth tracking
         // precisely.
         self.select_obligations_where_possible(|_| {});
-        self.resolve_vars_if_possible(ty)
+        self.resolve_vars_if_possible(t)
     }
 
     pub(crate) fn record_deferred_call_resolution(
@@ -1454,7 +1457,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         sp: Span,
         ct: ty::Const<'tcx>,
     ) -> ty::Const<'tcx> {
-        // FIXME(min_const_generic_exprs): We could process obligations here if `ct` is a var.
+        let ct = self.resolve_vars_with_obligations(ct);
 
         if self.next_trait_solver()
             && let ty::ConstKind::Unevaluated(..) = ct.kind()
@@ -1510,6 +1513,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    pub(crate) fn structurally_resolve_const(
+        &self,
+        sp: Span,
+        ct: ty::Const<'tcx>,
+    ) -> ty::Const<'tcx> {
+        let ct = self.try_structurally_resolve_const(sp, ct);
+
+        if !ct.is_ct_infer() {
+            ct
+        } else {
+            let e = self.tainted_by_errors().unwrap_or_else(|| {
+                self.err_ctxt()
+                    .emit_inference_failure_err(
+                        self.body_id,
+                        sp,
+                        ct.into(),
+                        TypeAnnotationNeeded::E0282,
+                        true,
+                    )
+                    .emit()
+            });
+            // FIXME: Infer `?ct = {const error}`?
+            ty::Const::new_error(self.tcx, e)
+        }
+    }
+
     pub(crate) fn with_breakable_ctxt<F: FnOnce() -> R, R>(
         &self,
         id: HirId,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 63c1c0608274a..e60a4bb47b575 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -110,8 +110,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     self.tcx.erase_regions(ty)
                 }
             };
-            InlineAsmCtxt::new(self.tcx, enclosing_id, self.typing_env(self.param_env), expr_ty)
-                .check_asm(asm);
+            let node_ty = |hir_id: HirId| self.typeck_results.borrow().node_type(hir_id);
+            InlineAsmCtxt::new(
+                self.tcx,
+                enclosing_id,
+                self.typing_env(self.param_env),
+                expr_ty,
+                node_ty,
+            )
+            .check_asm(asm);
+        }
+    }
+
+    pub(in super::super) fn check_repeat_exprs(&self) {
+        let mut deferred_repeat_expr_checks = self.deferred_repeat_expr_checks.borrow_mut();
+        debug!("FnCtxt::check_repeat_exprs: {} deferred checks", deferred_repeat_expr_checks.len());
+        for (element, element_ty, count) in deferred_repeat_expr_checks.drain(..) {
+            // We want to emit an error if the const is not structurally resolveable as otherwise
+            // we can find up conservatively proving `Copy` which may infer the repeat expr count
+            // to something that never required `Copy` in the first place.
+            let count =
+                self.structurally_resolve_const(element.span, self.normalize(element.span, count));
+
+            // Avoid run on "`NotCopy: Copy` is not implemented" errors when the repeat expr count
+            // is erroneous/unknown. The user might wind up specifying a repeat count of 0/1.
+            if count.references_error() {
+                continue;
+            }
+
+            // If the length is 0, we don't create any elements, so we don't copy any.
+            // If the length is 1, we don't copy that one element, we move it. Only check
+            // for `Copy` if the length is larger.
+            if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) {
+                self.enforce_repeat_element_needs_copy_bound(element, element_ty);
+            }
         }
     }
 
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 0130ad775d9e7..8b9c2b4a6ca0f 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -199,6 +199,15 @@ fn typeck_with_inspect<'tcx>(
         fcx.write_ty(id, expected_type);
     };
 
+    // Whether to check repeat exprs before/after inference fallback is somewhat arbitrary of a decision
+    // as neither option is strictly more permissive than the other. However, we opt to check repeat exprs
+    // first as errors from not having inferred array lengths yet seem less confusing than errors from inference
+    // fallback arbitrarily inferring something incompatible with `Copy` inference side effects.
+    //
+    // This should also be forwards compatible with moving repeat expr checks to a custom goal kind or using
+    // marker traits in the future.
+    fcx.check_repeat_exprs();
+
     fcx.type_inference_fallback();
 
     // Even though coercion casts provide type hints, we check casts after fallback for
diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs
index 903be7e732afa..381606a9fb020 100644
--- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs
+++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs
@@ -62,6 +62,9 @@ pub(crate) struct TypeckRootCtxt<'tcx> {
 
     pub(super) deferred_coroutine_interiors: RefCell<Vec<(LocalDefId, hir::BodyId, Ty<'tcx>)>>,
 
+    pub(super) deferred_repeat_expr_checks:
+        RefCell<Vec<(&'tcx hir::Expr<'tcx>, Ty<'tcx>, ty::Const<'tcx>)>>,
+
     /// Whenever we introduce an adjustment from `!` into a type variable,
     /// we record that type variable here. This is later used to inform
     /// fallback. See the `fallback` module for details.
@@ -96,6 +99,7 @@ impl<'tcx> TypeckRootCtxt<'tcx> {
             deferred_transmute_checks: RefCell::new(Vec::new()),
             deferred_asm_checks: RefCell::new(Vec::new()),
             deferred_coroutine_interiors: RefCell::new(Vec::new()),
+            deferred_repeat_expr_checks: RefCell::new(Vec::new()),
             diverging_type_vars: RefCell::new(Default::default()),
             infer_var_info: RefCell::new(Default::default()),
         }
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 3e9ce0e11e402..6c6538d09def0 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -248,13 +248,6 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
             }
         }
     }
-
-    fn visit_const_block(&mut self, span: Span, anon_const: &hir::ConstBlock) {
-        self.visit_node_id(span, anon_const.hir_id);
-
-        let body = self.tcx().hir_body(anon_const.body);
-        self.visit_body(body);
-    }
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -284,9 +277,6 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
             hir::ExprKind::Field(..) | hir::ExprKind::OffsetOf(..) => {
                 self.visit_field_id(e.hir_id);
             }
-            hir::ExprKind::ConstBlock(ref anon_const) => {
-                self.visit_const_block(e.span, anon_const);
-            }
             _ => {}
         }
 
@@ -297,6 +287,14 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
         self.fix_index_builtin_expr(e);
     }
 
+    fn visit_inline_const(&mut self, anon_const: &hir::ConstBlock) {
+        let span = self.tcx().def_span(anon_const.def_id);
+        self.visit_node_id(span, anon_const.hir_id);
+
+        let body = self.tcx().hir_body(anon_const.body);
+        self.visit_body(body);
+    }
+
     fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {
         match &p.kind {
             hir::GenericParamKind::Lifetime { .. } => {
@@ -343,9 +341,6 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
 
     fn visit_pat_expr(&mut self, expr: &'tcx hir::PatExpr<'tcx>) {
         self.visit_node_id(expr.span, expr.hir_id);
-        if let hir::PatExprKind::ConstBlock(c) = &expr.kind {
-            self.visit_const_block(expr.span, c);
-        }
         intravisit::walk_pat_expr(self, expr);
     }
 
diff --git a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs
index 0998f3c479024..a12d83db91f24 100644
--- a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs
+++ b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs
@@ -2,6 +2,7 @@
 //! the end of the file for details.
 
 use rustc_middle::ty::fold::FnMutDelegate;
+use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
 use tracing::{debug, instrument};
 
@@ -26,8 +27,9 @@ impl<'tcx> InferCtxt<'tcx> {
     where
         T: TypeFoldable<TyCtxt<'tcx>>,
     {
-        if let Some(inner) = binder.clone().no_bound_vars() {
-            return inner;
+        // Inlined `no_bound_vars`.
+        if !binder.as_ref().skip_binder().has_escaping_bound_vars() {
+            return binder.skip_binder();
         }
 
         let next_universe = self.create_next_universe();
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 00993c40dea93..96f621e0aa677 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -324,11 +324,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.features()
     }
 
-    fn bound_coroutine_hidden_types(
+    fn coroutine_hidden_types(
         self,
         def_id: DefId,
-    ) -> impl IntoIterator<Item = ty::EarlyBinder<'tcx, ty::Binder<'tcx, Ty<'tcx>>>> {
-        self.bound_coroutine_hidden_types(def_id)
+    ) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, &'tcx ty::List<Ty<'tcx>>>> {
+        self.coroutine_hidden_types(def_id)
     }
 
     fn fn_sig(self, def_id: DefId) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> {
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 4d917963cd6ce..237aa66f486f8 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -739,52 +739,38 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
-    /// Return the set of types that should be taken into account when checking
-    /// trait bounds on a coroutine's internal state.
-    // FIXME(compiler-errors): We should remove this when the old solver goes away;
-    // and all other usages of this function should go through `bound_coroutine_hidden_types`
-    // instead.
-    pub fn coroutine_hidden_types(
-        self,
-        def_id: DefId,
-    ) -> impl Iterator<Item = ty::EarlyBinder<'tcx, Ty<'tcx>>> {
-        let coroutine_layout = self.mir_coroutine_witnesses(def_id);
-        coroutine_layout
-            .as_ref()
-            .map_or_else(|| [].iter(), |l| l.field_tys.iter())
-            .filter(|decl| !decl.ignore_for_traits)
-            .map(|decl| ty::EarlyBinder::bind(decl.ty))
-    }
-
     /// Return the set of types that should be taken into account when checking
     /// trait bounds on a coroutine's internal state. This properly replaces
     /// `ReErased` with new existential bound lifetimes.
-    pub fn bound_coroutine_hidden_types(
+    pub fn coroutine_hidden_types(
         self,
         def_id: DefId,
-    ) -> impl Iterator<Item = ty::EarlyBinder<'tcx, ty::Binder<'tcx, Ty<'tcx>>>> {
+    ) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, &'tcx ty::List<Ty<'tcx>>>> {
         let coroutine_layout = self.mir_coroutine_witnesses(def_id);
-        coroutine_layout
-            .as_ref()
-            .map_or_else(|| [].iter(), |l| l.field_tys.iter())
-            .filter(|decl| !decl.ignore_for_traits)
-            .map(move |decl| {
-                let mut vars = vec![];
-                let ty = fold_regions(self, decl.ty, |re, debruijn| {
-                    assert_eq!(re, self.lifetimes.re_erased);
-                    let var = ty::BoundVar::from_usize(vars.len());
-                    vars.push(ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon));
-                    ty::Region::new_bound(
-                        self,
-                        debruijn,
-                        ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon },
-                    )
-                });
-                ty::EarlyBinder::bind(ty::Binder::bind_with_vars(
-                    ty,
-                    self.mk_bound_variable_kinds(&vars),
-                ))
-            })
+        let mut vars = vec![];
+        let bound_tys = self.mk_type_list_from_iter(
+            coroutine_layout
+                .as_ref()
+                .map_or_else(|| [].iter(), |l| l.field_tys.iter())
+                .filter(|decl| !decl.ignore_for_traits)
+                .map(|decl| {
+                    let ty = fold_regions(self, decl.ty, |re, debruijn| {
+                        assert_eq!(re, self.lifetimes.re_erased);
+                        let var = ty::BoundVar::from_usize(vars.len());
+                        vars.push(ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon));
+                        ty::Region::new_bound(
+                            self,
+                            debruijn,
+                            ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon },
+                        )
+                    });
+                    ty
+                }),
+        );
+        ty::EarlyBinder::bind(ty::Binder::bind_with_vars(
+            bound_tys,
+            self.mk_bound_variable_kinds(&vars),
+        ))
     }
 
     /// Expands the given impl trait type, stopping if the type is recursive.
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 88877d05ffadf..7139516702e99 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -730,12 +730,20 @@ impl<'tcx> ThirBuildCx<'tcx> {
                             }
                         }
                         hir::InlineAsmOperand::Const { ref anon_const } => {
-                            let value =
-                                mir::Const::from_unevaluated(tcx, anon_const.def_id.to_def_id())
-                                    .instantiate_identity();
-                            let span = tcx.def_span(anon_const.def_id);
-
-                            InlineAsmOperand::Const { value, span }
+                            let ty = self.typeck_results.node_type(anon_const.hir_id);
+                            let did = anon_const.def_id.to_def_id();
+                            let typeck_root_def_id = tcx.typeck_root_def_id(did);
+                            let parent_args = tcx.erase_regions(GenericArgs::identity_for_item(
+                                tcx,
+                                typeck_root_def_id,
+                            ));
+                            let args =
+                                InlineConstArgs::new(tcx, InlineConstArgsParts { parent_args, ty })
+                                    .args;
+
+                            let uneval = mir::UnevaluatedConst::new(did, args);
+                            let value = mir::Const::Unevaluated(uneval, ty);
+                            InlineAsmOperand::Const { value, span: tcx.def_span(did) }
                         }
                         hir::InlineAsmOperand::SymFn { expr } => {
                             InlineAsmOperand::SymFn { value: self.mirror_expr(expr) }
diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
index dc0f4c4483e6f..93804b14125dd 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
@@ -18,7 +18,7 @@ use crate::solve::{AdtDestructorKind, EvalCtxt, Goal, NoSolution};
 pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<D, I>(
     ecx: &EvalCtxt<'_, D>,
     ty: I::Ty,
-) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>
+) -> Result<ty::Binder<I, Vec<I::Ty>>, NoSolution>
 where
     D: SolverDelegate<Interner = I>,
     I: Interner,
@@ -33,10 +33,10 @@ where
         | ty::FnPtr(..)
         | ty::Error(_)
         | ty::Never
-        | ty::Char => Ok(vec![]),
+        | ty::Char => Ok(ty::Binder::dummy(vec![])),
 
         // Treat `str` like it's defined as `struct str([u8]);`
-        ty::Str => Ok(vec![ty::Binder::dummy(Ty::new_slice(cx, Ty::new_u8(cx)))]),
+        ty::Str => Ok(ty::Binder::dummy(vec![Ty::new_slice(cx, Ty::new_u8(cx))])),
 
         ty::Dynamic(..)
         | ty::Param(..)
@@ -49,53 +49,49 @@ where
         }
 
         ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => {
-            Ok(vec![ty::Binder::dummy(element_ty)])
+            Ok(ty::Binder::dummy(vec![element_ty]))
         }
 
         ty::Pat(element_ty, _) | ty::Array(element_ty, _) | ty::Slice(element_ty) => {
-            Ok(vec![ty::Binder::dummy(element_ty)])
+            Ok(ty::Binder::dummy(vec![element_ty]))
         }
 
         ty::Tuple(tys) => {
             // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet
-            Ok(tys.iter().map(ty::Binder::dummy).collect())
+            Ok(ty::Binder::dummy(tys.to_vec()))
         }
 
-        ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]),
+        ty::Closure(_, args) => Ok(ty::Binder::dummy(vec![args.as_closure().tupled_upvars_ty()])),
 
         ty::CoroutineClosure(_, args) => {
-            Ok(vec![ty::Binder::dummy(args.as_coroutine_closure().tupled_upvars_ty())])
+            Ok(ty::Binder::dummy(vec![args.as_coroutine_closure().tupled_upvars_ty()]))
         }
 
         ty::Coroutine(_, args) => {
             let coroutine_args = args.as_coroutine();
-            Ok(vec![
-                ty::Binder::dummy(coroutine_args.tupled_upvars_ty()),
-                ty::Binder::dummy(coroutine_args.witness()),
-            ])
+            Ok(ty::Binder::dummy(vec![coroutine_args.tupled_upvars_ty(), coroutine_args.witness()]))
         }
 
         ty::CoroutineWitness(def_id, args) => Ok(ecx
             .cx()
-            .bound_coroutine_hidden_types(def_id)
-            .into_iter()
-            .map(|bty| bty.instantiate(cx, args))
-            .collect()),
+            .coroutine_hidden_types(def_id)
+            .instantiate(cx, args)
+            .map_bound(|tys| tys.to_vec())),
 
-        ty::UnsafeBinder(bound_ty) => Ok(vec![bound_ty.into()]),
+        ty::UnsafeBinder(bound_ty) => Ok(bound_ty.map_bound(|ty| vec![ty])),
 
         // For `PhantomData<T>`, we pass `T`.
-        ty::Adt(def, args) if def.is_phantom_data() => Ok(vec![ty::Binder::dummy(args.type_at(0))]),
+        ty::Adt(def, args) if def.is_phantom_data() => Ok(ty::Binder::dummy(vec![args.type_at(0)])),
 
         ty::Adt(def, args) => {
-            Ok(def.all_field_tys(cx).iter_instantiated(cx, args).map(ty::Binder::dummy).collect())
+            Ok(ty::Binder::dummy(def.all_field_tys(cx).iter_instantiated(cx, args).collect()))
         }
 
         ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
             // We can resolve the `impl Trait` to its concrete type,
             // which enforces a DAG between the functions requiring
             // the auto trait bounds in question.
-            Ok(vec![ty::Binder::dummy(cx.type_of(def_id).instantiate(cx, args))])
+            Ok(ty::Binder::dummy(vec![cx.type_of(def_id).instantiate(cx, args)]))
         }
     }
 }
@@ -104,7 +100,7 @@ where
 pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<D, I>(
     ecx: &EvalCtxt<'_, D>,
     ty: I::Ty,
-) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>
+) -> Result<ty::Binder<I, Vec<I::Ty>>, NoSolution>
 where
     D: SolverDelegate<Interner = I>,
     I: Interner,
@@ -130,7 +126,7 @@ where
         | ty::CoroutineClosure(..)
         | ty::Never
         | ty::Dynamic(_, _, ty::DynStar)
-        | ty::Error(_) => Ok(vec![]),
+        | ty::Error(_) => Ok(ty::Binder::dummy(vec![])),
 
         ty::Str
         | ty::Slice(_)
@@ -145,11 +141,11 @@ where
             panic!("unexpected type `{ty:?}`")
         }
 
-        ty::UnsafeBinder(bound_ty) => Ok(vec![bound_ty.into()]),
+        ty::UnsafeBinder(bound_ty) => Ok(bound_ty.map_bound(|ty| vec![ty])),
 
         // impl Sized for ()
         // impl Sized for (T1, T2, .., Tn) where Tn: Sized if n >= 1
-        ty::Tuple(tys) => Ok(tys.last().map_or_else(Vec::new, |ty| vec![ty::Binder::dummy(ty)])),
+        ty::Tuple(tys) => Ok(ty::Binder::dummy(tys.last().map_or_else(Vec::new, |ty| vec![ty]))),
 
         // impl Sized for Adt<Args...> where sized_constraint(Adt)<Args...>: Sized
         //   `sized_constraint(Adt)` is the deepest struct trail that can be determined
@@ -162,9 +158,9 @@ where
         //   if the ADT is sized for all possible args.
         ty::Adt(def, args) => {
             if let Some(sized_crit) = def.sized_constraint(ecx.cx()) {
-                Ok(vec![ty::Binder::dummy(sized_crit.instantiate(ecx.cx(), args))])
+                Ok(ty::Binder::dummy(vec![sized_crit.instantiate(ecx.cx(), args)]))
             } else {
-                Ok(vec![])
+                Ok(ty::Binder::dummy(vec![]))
             }
         }
     }
@@ -174,14 +170,14 @@ where
 pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<D, I>(
     ecx: &EvalCtxt<'_, D>,
     ty: I::Ty,
-) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>
+) -> Result<ty::Binder<I, Vec<I::Ty>>, NoSolution>
 where
     D: SolverDelegate<Interner = I>,
     I: Interner,
 {
     match ty.kind() {
         // impl Copy/Clone for FnDef, FnPtr
-        ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Ok(vec![]),
+        ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Ok(ty::Binder::dummy(vec![])),
 
         // Implementations are provided in core
         ty::Uint(_)
@@ -197,7 +193,7 @@ where
 
         // Cannot implement in core, as we can't be generic over patterns yet,
         // so we'd have to list all patterns and type combinations.
-        ty::Pat(ty, ..) => Ok(vec![ty::Binder::dummy(ty)]),
+        ty::Pat(ty, ..) => Ok(ty::Binder::dummy(vec![ty])),
 
         ty::Dynamic(..)
         | ty::Str
@@ -215,14 +211,14 @@ where
         }
 
         // impl Copy/Clone for (T1, T2, .., Tn) where T1: Copy/Clone, T2: Copy/Clone, .. Tn: Copy/Clone
-        ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()),
+        ty::Tuple(tys) => Ok(ty::Binder::dummy(tys.to_vec())),
 
         // impl Copy/Clone for Closure where Self::TupledUpvars: Copy/Clone
-        ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]),
+        ty::Closure(_, args) => Ok(ty::Binder::dummy(vec![args.as_closure().tupled_upvars_ty()])),
 
         // impl Copy/Clone for CoroutineClosure where Self::TupledUpvars: Copy/Clone
         ty::CoroutineClosure(_, args) => {
-            Ok(vec![ty::Binder::dummy(args.as_coroutine_closure().tupled_upvars_ty())])
+            Ok(ty::Binder::dummy(vec![args.as_coroutine_closure().tupled_upvars_ty()]))
         }
 
         // only when `coroutine_clone` is enabled and the coroutine is movable
@@ -232,10 +228,7 @@ where
             Movability::Movable => {
                 if ecx.cx().features().coroutine_clone() {
                     let coroutine = args.as_coroutine();
-                    Ok(vec![
-                        ty::Binder::dummy(coroutine.tupled_upvars_ty()),
-                        ty::Binder::dummy(coroutine.witness()),
-                    ])
+                    Ok(ty::Binder::dummy(vec![coroutine.tupled_upvars_ty(), coroutine.witness()]))
                 } else {
                     Err(NoSolution)
                 }
@@ -247,10 +240,9 @@ where
         // impl Copy/Clone for CoroutineWitness where T: Copy/Clone forall T in coroutine_hidden_types
         ty::CoroutineWitness(def_id, args) => Ok(ecx
             .cx()
-            .bound_coroutine_hidden_types(def_id)
-            .into_iter()
-            .map(|bty| bty.instantiate(ecx.cx(), args))
-            .collect()),
+            .coroutine_hidden_types(def_id)
+            .instantiate(ecx.cx(), args)
+            .map_bound(|tys| tys.to_vec())),
     }
 }
 
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
index b0a34b9ce7560..491250f4a6912 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
@@ -913,7 +913,7 @@ where
 
     /// `enter_forall`, but takes `&mut self` and passes it back through the
     /// callback since it can't be aliased during the call.
-    pub(super) fn enter_forall<T: TypeFoldable<I> + Copy, U>(
+    pub(super) fn enter_forall<T: TypeFoldable<I>, U>(
         &mut self,
         value: ty::Binder<I, T>,
         f: impl FnOnce(&mut Self, T) -> U,
diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
index 1665dbb30189e..a34a56ae8c8e7 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -1239,17 +1239,15 @@ where
         constituent_tys: impl Fn(
             &EvalCtxt<'_, D>,
             I::Ty,
-        ) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>,
+        ) -> Result<ty::Binder<I, Vec<I::Ty>>, NoSolution>,
     ) -> Result<Candidate<I>, NoSolution> {
         self.probe_trait_candidate(source).enter(|ecx| {
-            let goals = constituent_tys(ecx, goal.predicate.self_ty())?
-                .into_iter()
-                .map(|ty| {
-                    ecx.enter_forall(ty, |ecx, ty| {
-                        goal.with(ecx.cx(), goal.predicate.with_self_ty(ecx.cx(), ty))
-                    })
-                })
-                .collect::<Vec<_>>();
+            let goals =
+                ecx.enter_forall(constituent_tys(ecx, goal.predicate.self_ty())?, |ecx, tys| {
+                    tys.into_iter()
+                        .map(|ty| goal.with(ecx.cx(), goal.predicate.with_self_ty(ecx.cx(), ty)))
+                        .collect::<Vec<_>>()
+                });
             ecx.add_goals(GoalSource::ImplWhereBound, goals);
             ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
         })
diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs
index 75972a71c8e57..9d78c71b76aa7 100644
--- a/compiler/rustc_resolve/src/def_collector.rs
+++ b/compiler/rustc_resolve/src/def_collector.rs
@@ -459,4 +459,43 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
         visit::walk_attribute(self, attr);
         self.in_attr = orig_in_attr;
     }
+
+    fn visit_inline_asm(&mut self, asm: &'a InlineAsm) {
+        let InlineAsm {
+            asm_macro: _,
+            template: _,
+            template_strs: _,
+            operands,
+            clobber_abis: _,
+            options: _,
+            line_spans: _,
+        } = asm;
+        for (op, _span) in operands {
+            match op {
+                InlineAsmOperand::In { expr, reg: _ }
+                | InlineAsmOperand::Out { expr: Some(expr), reg: _, late: _ }
+                | InlineAsmOperand::InOut { expr, reg: _, late: _ } => {
+                    self.visit_expr(expr);
+                }
+                InlineAsmOperand::Out { expr: None, reg: _, late: _ } => {}
+                InlineAsmOperand::SplitInOut { in_expr, out_expr, reg: _, late: _ } => {
+                    self.visit_expr(in_expr);
+                    if let Some(expr) = out_expr {
+                        self.visit_expr(expr);
+                    }
+                }
+                InlineAsmOperand::Const { anon_const } => {
+                    let def = self.create_def(
+                        anon_const.id,
+                        kw::Empty,
+                        DefKind::InlineConst,
+                        anon_const.value.span,
+                    );
+                    self.with_parent(def, |this| visit::walk_anon_const(this, anon_const));
+                }
+                InlineAsmOperand::Sym { sym } => self.visit_inline_asm_sym(sym),
+                InlineAsmOperand::Label { block } => self.visit_block(block),
+            }
+        }
+    }
 }
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 596f794568c5d..5c489a55aecfa 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
@@ -2419,6 +2419,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 &mut vec![],
                 &mut Default::default(),
             );
+            self.suggest_swapping_lhs_and_rhs(
+                err,
+                obligation.predicate,
+                obligation.param_env,
+                obligation.cause.code(),
+            );
             self.suggest_unsized_bound_if_applicable(err, obligation);
             if let Some(span) = err.span.primary_span()
                 && let Some(mut diag) =
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 0ff00e752a265..b85c18c53121f 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -4914,6 +4914,53 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         );
         true
     }
+    pub(crate) fn suggest_swapping_lhs_and_rhs<T>(
+        &self,
+        err: &mut Diag<'_>,
+        predicate: T,
+        param_env: ty::ParamEnv<'tcx>,
+        cause_code: &ObligationCauseCode<'tcx>,
+    ) where
+        T: Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>>,
+    {
+        let tcx = self.tcx;
+        let predicate = predicate.upcast(tcx);
+        match *cause_code {
+            ObligationCauseCode::BinOp {
+                lhs_hir_id,
+                rhs_hir_id: Some(rhs_hir_id),
+                rhs_span: Some(rhs_span),
+                ..
+            } if let Some(typeck_results) = &self.typeck_results
+                && let hir::Node::Expr(lhs) = tcx.hir_node(lhs_hir_id)
+                && let hir::Node::Expr(rhs) = tcx.hir_node(rhs_hir_id)
+                && let Some(lhs_ty) = typeck_results.expr_ty_opt(lhs)
+                && let Some(rhs_ty) = typeck_results.expr_ty_opt(rhs) =>
+            {
+                if let Some(pred) = predicate.as_trait_clause()
+                    && tcx.is_lang_item(pred.def_id(), LangItem::PartialEq)
+                    && self
+                        .infcx
+                        .type_implements_trait(pred.def_id(), [rhs_ty, lhs_ty], param_env)
+                        .must_apply_modulo_regions()
+                {
+                    let lhs_span = tcx.hir().span(lhs_hir_id);
+                    let sm = tcx.sess.source_map();
+                    if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_span)
+                        && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_span)
+                    {
+                        err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`"));
+                        err.multipart_suggestion(
+                            "consider swapping the equality",
+                            vec![(lhs_span, rhs_snippet), (rhs_span, lhs_snippet)],
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+            }
+            _ => {}
+        }
+    }
 }
 
 /// Add a hint to add a missing borrow or remove an unnecessary one.
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 436ce3dddd9f0..dead95e8475b1 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -9,7 +9,7 @@ use std::ops::ControlFlow;
 use std::{cmp, iter};
 
 use hir::def::DefKind;
-use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
+use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{Diag, EmissionGuarantee};
 use rustc_hir as hir;
@@ -25,7 +25,6 @@ use rustc_middle::dep_graph::{DepNodeIndex, dep_kinds};
 pub use rustc_middle::traits::select::*;
 use rustc_middle::ty::abstract_const::NotConstEvaluatable;
 use rustc_middle::ty::error::TypeErrorToStringExt;
-use rustc_middle::ty::fold::fold_regions;
 use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths};
 use rustc_middle::ty::{
     self, GenericArgsRef, PolyProjectionPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitableExt,
@@ -2199,8 +2198,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             }
 
             ty::CoroutineWitness(def_id, args) => {
-                let hidden_types = bind_coroutine_hidden_types_above(
-                    self.infcx,
+                let hidden_types = rebind_coroutine_witness_types(
+                    self.infcx.tcx,
                     def_id,
                     args,
                     obligation.predicate.bound_vars(),
@@ -2348,7 +2347,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             }
 
             ty::CoroutineWitness(def_id, args) => {
-                bind_coroutine_hidden_types_above(self.infcx, def_id, args, t.bound_vars())
+                rebind_coroutine_witness_types(self.infcx.tcx, def_id, args, t.bound_vars())
             }
 
             // For `PhantomData<T>`, we pass `T`.
@@ -2843,6 +2842,23 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
     }
 }
 
+fn rebind_coroutine_witness_types<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    args: ty::GenericArgsRef<'tcx>,
+    bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
+) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> {
+    let bound_coroutine_types = tcx.coroutine_hidden_types(def_id).skip_binder();
+    let shifted_coroutine_types =
+        tcx.shift_bound_var_indices(bound_vars.len(), bound_coroutine_types.skip_binder());
+    ty::Binder::bind_with_vars(
+        ty::EarlyBinder::bind(shifted_coroutine_types.to_vec()).instantiate(tcx, args),
+        tcx.mk_bound_variable_kinds_from_iter(
+            bound_vars.iter().chain(bound_coroutine_types.bound_vars()),
+        ),
+    )
+}
+
 impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> {
     fn list(&'o self) -> TraitObligationStackList<'o, 'tcx> {
         TraitObligationStackList::with(self)
@@ -3151,56 +3167,3 @@ pub(crate) enum ProjectionMatchesProjection {
     Ambiguous,
     No,
 }
-
-/// Replace all regions inside the coroutine interior with late bound regions.
-/// Note that each region slot in the types gets a new fresh late bound region, which means that
-/// none of the regions inside relate to any other, even if typeck had previously found constraints
-/// that would cause them to be related.
-#[instrument(level = "trace", skip(infcx), ret)]
-fn bind_coroutine_hidden_types_above<'tcx>(
-    infcx: &InferCtxt<'tcx>,
-    def_id: DefId,
-    args: ty::GenericArgsRef<'tcx>,
-    bound_vars: &ty::List<ty::BoundVariableKind>,
-) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> {
-    let tcx = infcx.tcx;
-    let mut seen_tys = FxHashSet::default();
-
-    let considering_regions = infcx.considering_regions;
-
-    let num_bound_variables = bound_vars.len() as u32;
-    let mut counter = num_bound_variables;
-
-    let hidden_types: Vec<_> = tcx
-        .coroutine_hidden_types(def_id)
-        // Deduplicate tys to avoid repeated work.
-        .filter(|bty| seen_tys.insert(*bty))
-        .map(|mut bty| {
-            // Only remap erased regions if we use them.
-            if considering_regions {
-                bty = bty.map_bound(|ty| {
-                    fold_regions(tcx, ty, |r, current_depth| match r.kind() {
-                        ty::ReErased => {
-                            let br = ty::BoundRegion {
-                                var: ty::BoundVar::from_u32(counter),
-                                kind: ty::BoundRegionKind::Anon,
-                            };
-                            counter += 1;
-                            ty::Region::new_bound(tcx, current_depth, br)
-                        }
-                        r => bug!("unexpected region: {r:?}"),
-                    })
-                })
-            }
-
-            bty.instantiate(tcx, args)
-        })
-        .collect();
-    let bound_vars = tcx.mk_bound_variable_kinds_from_iter(
-        bound_vars.iter().chain(
-            (num_bound_variables..counter)
-                .map(|_| ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon)),
-        ),
-    );
-    ty::Binder::bind_with_vars(hidden_types, bound_vars)
-}
diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs
index c9b636132f807..d5edc9610a4af 100644
--- a/compiler/rustc_type_ir/src/infer_ctxt.rs
+++ b/compiler/rustc_type_ir/src/infer_ctxt.rs
@@ -151,7 +151,7 @@ pub trait InferCtxtLike: Sized {
         value: ty::Binder<Self::Interner, T>,
     ) -> T;
 
-    fn enter_forall<T: TypeFoldable<Self::Interner> + Copy, U>(
+    fn enter_forall<T: TypeFoldable<Self::Interner>, U>(
         &self,
         value: ty::Binder<Self::Interner, T>,
         f: impl FnOnce(T) -> U,
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index aae2d2e96b96f..584f3985624fd 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -189,10 +189,10 @@ pub trait Interner:
     type Features: Features<Self>;
     fn features(self) -> Self::Features;
 
-    fn bound_coroutine_hidden_types(
+    fn coroutine_hidden_types(
         self,
         def_id: Self::DefId,
-    ) -> impl IntoIterator<Item = ty::EarlyBinder<Self, ty::Binder<Self, Self::Ty>>>;
+    ) -> ty::EarlyBinder<Self, ty::Binder<Self, Self::Tys>>;
 
     fn fn_sig(
         self,
diff --git a/config.example.toml b/config.example.toml
index a17d3ec9f8841..c4b0822eeb8e9 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -189,6 +189,17 @@
 # The default stage to use for the `bench` subcommand
 #bench-stage = 2
 
+# A descriptive string to be appended to version output (e.g., `rustc --version`),
+# which is also used in places like debuginfo `DW_AT_producer`. This may be useful for
+# supplementary build information, like distro-specific package versions.
+#
+# The Rust compiler will differentiate between versions of itself, including
+# based on this string, which means that if you wish to be compatible with
+# upstream Rust you need to set this to "". However, note that if you set this to "" but
+# are not actually compatible -- for example if you've backported patches that change
+# behavior -- this may lead to miscompilations or other bugs.
+#description = ""
+
 # Build triple for the pre-compiled snapshot compiler. If `rustc` is set, this must match its host
 # triple (see `rustc --version --verbose`; cross-compiling the rust build system itself is NOT
 # supported). If `rustc` is unset, this must be a platform with pre-compiled host tools
@@ -614,17 +625,6 @@
 # otherwise, it's "dev".
 #channel = if "is a tarball source" { content of `src/ci/channel` file } else { "dev" }
 
-# A descriptive string to be appended to `rustc --version` output, which is
-# also used in places like debuginfo `DW_AT_producer`. This may be useful for
-# supplementary build information, like distro-specific package versions.
-#
-# The Rust compiler will differentiate between versions of itself, including
-# based on this string, which means that if you wish to be compatible with
-# upstream Rust you need to set this to "". However, note that if you are not
-# actually compatible -- for example if you've backported patches that change
-# behavior -- this may lead to miscompilations or other bugs.
-#description = ""
-
 # The root location of the musl installation directory. The library directory
 # will also need to contain libunwind.a for an unwinding implementation. Note
 # that this option only makes sense for musl targets that produce statically
diff --git a/library/coretests/tests/num/int_log.rs b/library/coretests/tests/num/int_log.rs
index 9c630a61dd5b3..e8d35fc21ce6e 100644
--- a/library/coretests/tests/num/int_log.rs
+++ b/library/coretests/tests/num/int_log.rs
@@ -1,5 +1,13 @@
 //! Tests for the `Integer::{ilog,log2,log10}` methods.
 
+/// Rounds the argument down to the next integer, except that we account for potential imprecision
+/// in the input, so if `f` is very close to an integer, it will round to that.
+fn round_down_imprecise(f: f32) -> u32 {
+    // Rounds up for values less than 16*EPSILON below an integer,
+    // and rounds down for everything else.
+    (f + 16.0 * f32::EPSILON) as u32
+}
+
 #[test]
 fn checked_ilog() {
     assert_eq!(999u32.checked_ilog(10), Some(2));
@@ -25,11 +33,19 @@ fn checked_ilog() {
     }
     #[cfg(not(miri))] // Miri is too slow
     for i in 1..=i16::MAX {
-        assert_eq!(i.checked_ilog(13), Some((i as f32).log(13.0) as u32), "checking {i}");
+        assert_eq!(
+            i.checked_ilog(13),
+            Some(round_down_imprecise((i as f32).log(13.0))),
+            "checking {i}"
+        );
     }
     #[cfg(not(miri))] // Miri is too slow
     for i in 1..=u16::MAX {
-        assert_eq!(i.checked_ilog(13), Some((i as f32).log(13.0) as u32), "checking {i}");
+        assert_eq!(
+            i.checked_ilog(13),
+            Some(round_down_imprecise((i as f32).log(13.0))),
+            "checking {i}"
+        );
     }
 }
 
@@ -46,25 +62,34 @@ fn checked_ilog2() {
     assert_eq!(0i8.checked_ilog2(), None);
     assert_eq!(0i16.checked_ilog2(), None);
 
-    assert_eq!(8192u16.checked_ilog2(), Some((8192f32).log2() as u32));
-    assert_eq!(32768u16.checked_ilog2(), Some((32768f32).log2() as u32));
-    assert_eq!(8192i16.checked_ilog2(), Some((8192f32).log2() as u32));
+    assert_eq!(8192u16.checked_ilog2(), Some(round_down_imprecise((8192f32).log2())));
+    assert_eq!(32768u16.checked_ilog2(), Some(round_down_imprecise((32768f32).log2())));
+    assert_eq!(8192i16.checked_ilog2(), Some(round_down_imprecise((8192f32).log2())));
 
     for i in 1..=u8::MAX {
-        assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32), "checking {i}");
+        assert_eq!(
+            i.checked_ilog2(),
+            Some(round_down_imprecise((i as f32).log2())),
+            "checking {i}"
+        );
     }
     #[cfg(not(miri))] // Miri is too slow
     for i in 1..=u16::MAX {
-        // Guard against Android's imprecise f32::ilog2 implementation.
-        if i != 8192 && i != 32768 {
-            assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32), "checking {i}");
-        }
+        assert_eq!(
+            i.checked_ilog2(),
+            Some(round_down_imprecise((i as f32).log2())),
+            "checking {i}"
+        );
     }
     for i in i8::MIN..=0 {
         assert_eq!(i.checked_ilog2(), None, "checking {i}");
     }
     for i in 1..=i8::MAX {
-        assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32), "checking {i}");
+        assert_eq!(
+            i.checked_ilog2(),
+            Some(round_down_imprecise((i as f32).log2())),
+            "checking {i}"
+        );
     }
     #[cfg(not(miri))] // Miri is too slow
     for i in i16::MIN..=0 {
@@ -72,10 +97,11 @@ fn checked_ilog2() {
     }
     #[cfg(not(miri))] // Miri is too slow
     for i in 1..=i16::MAX {
-        // Guard against Android's imprecise f32::ilog2 implementation.
-        if i != 8192 {
-            assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32), "checking {i}");
-        }
+        assert_eq!(
+            i.checked_ilog2(),
+            Some(round_down_imprecise((i as f32).log2())),
+            "checking {i}"
+        );
     }
 }
 
@@ -92,15 +118,27 @@ fn checked_ilog10() {
     }
     #[cfg(not(miri))] // Miri is too slow
     for i in 1..=i16::MAX {
-        assert_eq!(i.checked_ilog10(), Some((i as f32).log10() as u32), "checking {i}");
+        assert_eq!(
+            i.checked_ilog10(),
+            Some(round_down_imprecise((i as f32).log10())),
+            "checking {i}"
+        );
     }
     #[cfg(not(miri))] // Miri is too slow
     for i in 1..=u16::MAX {
-        assert_eq!(i.checked_ilog10(), Some((i as f32).log10() as u32), "checking {i}");
+        assert_eq!(
+            i.checked_ilog10(),
+            Some(round_down_imprecise((i as f32).log10())),
+            "checking {i}"
+        );
     }
     #[cfg(not(miri))] // Miri is too slow
     for i in 1..=100_000u32 {
-        assert_eq!(i.checked_ilog10(), Some((i as f32).log10() as u32), "checking {i}");
+        assert_eq!(
+            i.checked_ilog10(),
+            Some(round_down_imprecise((i as f32).log10())),
+            "checking {i}"
+        );
     }
 }
 
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index e3f58d97cbc4c..c3b256464396f 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -292,7 +292,7 @@ def v(*args):
 v("release-channel", "rust.channel", "the name of the release channel to build")
 v(
     "release-description",
-    "rust.description",
+    "build.description",
     "optional descriptive string for version output",
 )
 v("dist-compression-formats", None, "List of compression formats to use")
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 39acb646dff4d..44d04a54bcc8e 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -234,23 +234,32 @@ pub fn prepare_tool_cargo(
     cargo.env("CFG_VERSION", builder.rust_version());
     cargo.env("CFG_RELEASE_NUM", &builder.version);
     cargo.env("DOC_RUST_LANG_ORG_CHANNEL", builder.doc_rust_lang_org_channel());
+
     if let Some(ref ver_date) = builder.rust_info().commit_date() {
         cargo.env("CFG_VER_DATE", ver_date);
     }
+
     if let Some(ref ver_hash) = builder.rust_info().sha() {
         cargo.env("CFG_VER_HASH", ver_hash);
     }
 
+    if let Some(description) = &builder.config.description {
+        cargo.env("CFG_VER_DESCRIPTION", description);
+    }
+
     let info = GitInfo::new(builder.config.omit_git_hash, &dir);
     if let Some(sha) = info.sha() {
         cargo.env("CFG_COMMIT_HASH", sha);
     }
+
     if let Some(sha_short) = info.sha_short() {
         cargo.env("CFG_SHORT_COMMIT_HASH", sha_short);
     }
+
     if let Some(date) = info.commit_date() {
         cargo.env("CFG_COMMIT_DATE", date);
     }
+
     if !features.is_empty() {
         cargo.arg("--features").arg(features.join(", "));
     }
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index 2be42f16e2ab6..c088ec3c8f533 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -894,6 +894,7 @@ define_config! {
     #[derive(Default)]
     struct Build {
         build: Option<String> = "build",
+        description: Option<String> = "description",
         host: Option<Vec<String>> = "host",
         target: Option<Vec<String>> = "target",
         build_dir: Option<String> = "build-dir",
@@ -1176,6 +1177,7 @@ define_config! {
         incremental: Option<bool> = "incremental",
         default_linker: Option<String> = "default-linker",
         channel: Option<String> = "channel",
+        // FIXME: Remove this field at Q2 2025, it has been replaced by build.description
         description: Option<String> = "description",
         musl_root: Option<String> = "musl-root",
         rpath: Option<bool> = "rpath",
@@ -1583,6 +1585,7 @@ impl Config {
         config.change_id = toml.change_id.inner;
 
         let Build {
+            mut description,
             build,
             host,
             target,
@@ -1827,7 +1830,7 @@ impl Config {
                 randomize_layout,
                 default_linker,
                 channel: _, // already handled above
-                description,
+                description: rust_description,
                 musl_root,
                 rpath,
                 verbose_tests,
@@ -1920,7 +1923,12 @@ impl Config {
             set(&mut config.jemalloc, jemalloc);
             set(&mut config.test_compare_mode, test_compare_mode);
             set(&mut config.backtrace, backtrace);
-            config.description = description;
+            if rust_description.is_some() {
+                eprintln!(
+                    "Warning: rust.description is deprecated. Use build.description instead."
+                );
+            }
+            description = description.or(rust_description);
             set(&mut config.rust_dist_src, dist_src);
             set(&mut config.verbose_tests, verbose_tests);
             // in the case "false" is set explicitly, do not overwrite the command line args
@@ -1986,6 +1994,7 @@ impl Config {
         }
 
         config.reproducible_artifacts = flags.reproducible_artifact;
+        config.description = description;
 
         // We need to override `rust.channel` if it's manually specified when using the CI rustc.
         // This is because if the compiler uses a different channel than the one specified in config.toml,
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index 8dfe0d3a35ef6..b5755a8021ffb 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -360,4 +360,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         severity: ChangeSeverity::Info,
         summary: "Added `build.test-stage = 2` to 'tools' profile defaults",
     },
+    ChangeInfo {
+        change_id: 137723,
+        severity: ChangeSeverity::Info,
+        summary: "The rust.description option has moved to build.description and rust.description is now deprecated.",
+    },
 ];
diff --git a/src/ci/run.sh b/src/ci/run.sh
index 536754f12bc6d..b874f71832d73 100755
--- a/src/ci/run.sh
+++ b/src/ci/run.sh
@@ -279,5 +279,5 @@ if [ "$RUN_CHECK_WITH_PARALLEL_QUERIES" != "" ]; then
 fi
 
 echo "::group::sccache stats"
-sccache --show-adv-stats || true
+sccache --show-stats || true
 echo "::endgroup::"
diff --git a/tests/crashes/117877.rs b/tests/crashes/117877.rs
deleted file mode 100644
index b1effc0cbcb1a..0000000000000
--- a/tests/crashes/117877.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-//@ known-bug: #117877
-//@ edition:2021
-//@ needs-rustc-debug-assertions
-//@ only-x86_64
-#![feature(asm_const)]
-
-use std::arch::asm;
-
-async unsafe fn foo<'a>() {
-    asm!("/* {0} */", const N);
-}
-
-fn main() {}
diff --git a/tests/ui/abi/riscv-discoverability-guidance.riscv32.stderr b/tests/ui/abi/riscv-discoverability-guidance.riscv32.stderr
index e1f433479857a..efa70df9277ac 100644
--- a/tests/ui/abi/riscv-discoverability-guidance.riscv32.stderr
+++ b/tests/ui/abi/riscv-discoverability-guidance.riscv32.stderr
@@ -2,23 +2,26 @@ error[E0703]: invalid ABI: found `riscv-interrupt`
   --> $DIR/riscv-discoverability-guidance.rs:17:8
    |
 LL | extern "riscv-interrupt" fn isr() {}
-   |        ^^^^^^^^^^^^^^^^^
-   |        |
-   |        invalid ABI
-   |        help: did you mean: `"riscv-interrupt-m"`
+   |        ^^^^^^^^^^^^^^^^^ invalid ABI
    |
    = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions
+help: there's a similarly named valid ABI `riscv-interrupt-m`
+   |
+LL | extern "riscv-interrupt-m" fn isr() {}
+   |                        ++
 
 error[E0703]: invalid ABI: found `riscv-interrupt-u`
   --> $DIR/riscv-discoverability-guidance.rs:22:8
    |
 LL | extern "riscv-interrupt-u" fn isr_U() {}
-   |        ^^^^^^^^^^^^^^^^^^^
-   |        |
-   |        invalid ABI
-   |        help: did you mean: `"riscv-interrupt-m"`
+   |        ^^^^^^^^^^^^^^^^^^^ invalid ABI
    |
    = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions
+help: there's a similarly named valid ABI `riscv-interrupt-m`
+   |
+LL - extern "riscv-interrupt-u" fn isr_U() {}
+LL + extern "riscv-interrupt-m" fn isr_U() {}
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/abi/riscv-discoverability-guidance.riscv64.stderr b/tests/ui/abi/riscv-discoverability-guidance.riscv64.stderr
index e1f433479857a..efa70df9277ac 100644
--- a/tests/ui/abi/riscv-discoverability-guidance.riscv64.stderr
+++ b/tests/ui/abi/riscv-discoverability-guidance.riscv64.stderr
@@ -2,23 +2,26 @@ error[E0703]: invalid ABI: found `riscv-interrupt`
   --> $DIR/riscv-discoverability-guidance.rs:17:8
    |
 LL | extern "riscv-interrupt" fn isr() {}
-   |        ^^^^^^^^^^^^^^^^^
-   |        |
-   |        invalid ABI
-   |        help: did you mean: `"riscv-interrupt-m"`
+   |        ^^^^^^^^^^^^^^^^^ invalid ABI
    |
    = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions
+help: there's a similarly named valid ABI `riscv-interrupt-m`
+   |
+LL | extern "riscv-interrupt-m" fn isr() {}
+   |                        ++
 
 error[E0703]: invalid ABI: found `riscv-interrupt-u`
   --> $DIR/riscv-discoverability-guidance.rs:22:8
    |
 LL | extern "riscv-interrupt-u" fn isr_U() {}
-   |        ^^^^^^^^^^^^^^^^^^^
-   |        |
-   |        invalid ABI
-   |        help: did you mean: `"riscv-interrupt-m"`
+   |        ^^^^^^^^^^^^^^^^^^^ invalid ABI
    |
    = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions
+help: there's a similarly named valid ABI `riscv-interrupt-m`
+   |
+LL - extern "riscv-interrupt-u" fn isr_U() {}
+LL + extern "riscv-interrupt-m" fn isr_U() {}
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/asm/const-resolve-error.rs b/tests/ui/asm/const-resolve-error.rs
new file mode 100644
index 0000000000000..19c8af0d542ef
--- /dev/null
+++ b/tests/ui/asm/const-resolve-error.rs
@@ -0,0 +1,10 @@
+//@ edition:2021
+//@ needs-asm-support
+
+use std::arch::asm;
+
+async unsafe fn foo<'a>() {
+    asm!("/* {0} */", const N); //~ ERROR E0425
+}
+
+fn main() {}
diff --git a/tests/ui/asm/const-resolve-error.stderr b/tests/ui/asm/const-resolve-error.stderr
new file mode 100644
index 0000000000000..f02a7f0a6b127
--- /dev/null
+++ b/tests/ui/asm/const-resolve-error.stderr
@@ -0,0 +1,14 @@
+error[E0425]: cannot find value `N` in this scope
+  --> $DIR/const-resolve-error.rs:7:29
+   |
+LL |     asm!("/* {0} */", const N);
+   |                             ^ not found in this scope
+   |
+help: you might be missing a const parameter
+   |
+LL | async unsafe fn foo<'a, const N: /* Type */>() {
+   |                       +++++++++++++++++++++
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0425`.
diff --git a/tests/ui/asm/fail-const-eval-issue-121099.rs b/tests/ui/asm/fail-const-eval-issue-121099.rs
index 36d00b1e5d23c..c91bbfd1b73f9 100644
--- a/tests/ui/asm/fail-const-eval-issue-121099.rs
+++ b/tests/ui/asm/fail-const-eval-issue-121099.rs
@@ -5,6 +5,6 @@ use std::arch::global_asm;
 
 fn main() {}
 
-global_asm!("/* {} */", const 1 << 500); //~ ERROR evaluation of constant value failed [E0080]
+global_asm!("/* {} */", const 1 << 500); //~ ERROR E0080
 
-global_asm!("/* {} */", const 1 / 0); //~ ERROR evaluation of constant value failed [E0080]
+global_asm!("/* {} */", const 1 / 0); //~ ERROR E0080
diff --git a/tests/ui/asm/fail-const-eval-issue-121099.stderr b/tests/ui/asm/fail-const-eval-issue-121099.stderr
index 5d86c3a5f7bdd..eb662dadffb1b 100644
--- a/tests/ui/asm/fail-const-eval-issue-121099.stderr
+++ b/tests/ui/asm/fail-const-eval-issue-121099.stderr
@@ -1,10 +1,10 @@
-error[E0080]: evaluation of constant value failed
+error[E0080]: evaluation of `{global_asm#0}::{constant#0}` failed
   --> $DIR/fail-const-eval-issue-121099.rs:8:31
    |
 LL | global_asm!("/* {} */", const 1 << 500);
    |                               ^^^^^^^^ attempt to shift left by `500_i32`, which would overflow
 
-error[E0080]: evaluation of constant value failed
+error[E0080]: evaluation of `{global_asm#1}::{constant#0}` failed
   --> $DIR/fail-const-eval-issue-121099.rs:10:31
    |
 LL | global_asm!("/* {} */", const 1 / 0);
diff --git a/tests/ui/asm/invalid-const-operand.stderr b/tests/ui/asm/invalid-const-operand.stderr
index 13bb10e84a533..1cedabeef2892 100644
--- a/tests/ui/asm/invalid-const-operand.stderr
+++ b/tests/ui/asm/invalid-const-operand.stderr
@@ -80,7 +80,7 @@ error: invalid type for `const` operand
 LL |         asm!("{}", const &0);
    |                    ^^^^^^--
    |                          |
-   |                          is a `&i32`
+   |                          is a `&{integer}`
    |
    = help: `const` operands must be of an integer type
 
diff --git a/tests/ui/consts/const-fn-in-vec.rs b/tests/ui/consts/const-fn-in-vec.rs
index 0483800efefd1..d1430bc8e002c 100644
--- a/tests/ui/consts/const-fn-in-vec.rs
+++ b/tests/ui/consts/const-fn-in-vec.rs
@@ -1,11 +1,16 @@
 static _MAYBE_STRINGS: [Option<String>; 5] = [None; 5];
 //~^ ERROR the trait bound `String: Copy` is not satisfied
 
-fn main() {
-    // should hint to create an inline `const` block
-    // or to create a new `const` item
+// should hint to create an inline `const` block
+// or to create a new `const` item
+fn foo() {
     let _strings: [String; 5] = [String::new(); 5];
     //~^ ERROR the trait bound `String: Copy` is not satisfied
+}
+
+fn bar() {
     let _maybe_strings: [Option<String>; 5] = [None; 5];
     //~^ ERROR the trait bound `String: Copy` is not satisfied
 }
+
+fn main() {}
diff --git a/tests/ui/consts/const-fn-in-vec.stderr b/tests/ui/consts/const-fn-in-vec.stderr
index b31e180fea2b5..5be26d7c121bb 100644
--- a/tests/ui/consts/const-fn-in-vec.stderr
+++ b/tests/ui/consts/const-fn-in-vec.stderr
@@ -22,7 +22,7 @@ LL |     let _strings: [String; 5] = [String::new(); 5];
    = note: the `Copy` trait is required because this value will be copied for each element of the array
 
 error[E0277]: the trait bound `String: Copy` is not satisfied
-  --> $DIR/const-fn-in-vec.rs:9:48
+  --> $DIR/const-fn-in-vec.rs:12:48
    |
 LL |     let _maybe_strings: [Option<String>; 5] = [None; 5];
    |                                                ^^^^
diff --git a/tests/ui/never_type/unused_trait_in_never_pattern_body.rs b/tests/ui/never_type/unused_trait_in_never_pattern_body.rs
new file mode 100644
index 0000000000000..8179c94b72b23
--- /dev/null
+++ b/tests/ui/never_type/unused_trait_in_never_pattern_body.rs
@@ -0,0 +1,12 @@
+fn a() {
+    match 0 {
+        ! => || { //~ ERROR `!` patterns are experimental
+        //~^ ERROR a never pattern is always unreachable
+        //~^^ ERROR mismatched types
+            use std::ops::Add;
+            0.add(1)
+        },
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/never_type/unused_trait_in_never_pattern_body.stderr b/tests/ui/never_type/unused_trait_in_never_pattern_body.stderr
new file mode 100644
index 0000000000000..18ca9f12b7e81
--- /dev/null
+++ b/tests/ui/never_type/unused_trait_in_never_pattern_body.stderr
@@ -0,0 +1,36 @@
+error[E0658]: `!` patterns are experimental
+  --> $DIR/unused_trait_in_never_pattern_body.rs:3:9
+   |
+LL |         ! => || {
+   |         ^
+   |
+   = note: see issue #118155 <https://github.com/rust-lang/rust/issues/118155> for more information
+   = help: add `#![feature(never_patterns)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: a never pattern is always unreachable
+  --> $DIR/unused_trait_in_never_pattern_body.rs:3:14
+   |
+LL |           ! => || {
+   |  ______________^
+LL | |
+LL | |
+LL | |             use std::ops::Add;
+LL | |             0.add(1)
+LL | |         },
+   | |         ^
+   | |         |
+   | |_________this will never be executed
+   |           help: remove this expression
+
+error: mismatched types
+  --> $DIR/unused_trait_in_never_pattern_body.rs:3:9
+   |
+LL |         ! => || {
+   |         ^ a never pattern must be used on an uninhabited type
+   |
+   = note: the matched value is of type `i32`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/repeat-expr/copy-check-deferred-after-fallback.rs b/tests/ui/repeat-expr/copy-check-deferred-after-fallback.rs
new file mode 100644
index 0000000000000..d9ad93541ecff
--- /dev/null
+++ b/tests/ui/repeat-expr/copy-check-deferred-after-fallback.rs
@@ -0,0 +1,37 @@
+#![feature(generic_arg_infer)]
+
+// Test that would start passing if we defer repeat expr copy checks to end of
+// typechecking and they're checked after integer fallback occurs. We accomplish
+// this by contriving a situation where integer fallback allows progress to be
+// made on a trait goal that infers the length of a repeat expr.
+
+use std::marker::PhantomData;
+
+struct NotCopy;
+
+trait Trait<const N: usize> {}
+
+impl Trait<2> for u32 {}
+impl Trait<1> for i32 {}
+
+fn make_goal<T: Trait<N>, const N: usize>(_: &T, _: [NotCopy; N]) {}
+
+fn main() {
+    let a = 1;
+    let b = [NotCopy; _];
+    //~^ ERROR: type annotations needed
+
+    // a is of type `?y`
+    // b is of type `[NotCopy; ?x]`
+    // there is a goal ?y: Trait<?x>` with two candidates:
+    // - `i32: Trait<1>`, ?y=i32 ?x=1 which doesnt require `NotCopy: Copy`
+    // - `u32: Trait<2>` ?y=u32 ?x=2 which requires `NotCopy: Copy`
+    make_goal(&a, b);
+
+    // final repeat expr checks:
+    //
+    // `NotCopy; ?x`
+    // - succeeds if fallback happens before repeat exprs as `i32: Trait<?x>` infers `?x=1`
+    // - fails if repeat expr checks happen first as `?x` is unconstrained so cannot be
+    //    structurally resolved
+}
diff --git a/tests/ui/repeat-expr/copy-check-deferred-after-fallback.stderr b/tests/ui/repeat-expr/copy-check-deferred-after-fallback.stderr
new file mode 100644
index 0000000000000..2a0cb3fb7a394
--- /dev/null
+++ b/tests/ui/repeat-expr/copy-check-deferred-after-fallback.stderr
@@ -0,0 +1,14 @@
+error[E0282]: type annotations needed for `[NotCopy; _]`
+  --> $DIR/copy-check-deferred-after-fallback.rs:21:9
+   |
+LL |     let b = [NotCopy; _];
+   |         ^    ------- type must be known at this point
+   |
+help: consider giving `b` an explicit type, where the value of const parameter `N` is specified
+   |
+LL |     let b: [_; N] = [NotCopy; _];
+   |          ++++++++
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/repeat-expr/copy-check-deferred-before-fallback.rs b/tests/ui/repeat-expr/copy-check-deferred-before-fallback.rs
new file mode 100644
index 0000000000000..4654d7483a64d
--- /dev/null
+++ b/tests/ui/repeat-expr/copy-check-deferred-before-fallback.rs
@@ -0,0 +1,59 @@
+//@ check-pass
+
+#![feature(generic_arg_infer)]
+
+// Test that if we defer repeat expr copy checks to end of typechecking they're
+// checked before integer fallback occurs. We accomplish this by contriving a
+// situation where we have a goal that can be proven either via another repeat expr
+// check or by integer fallback. In the integer fallback case an array length would
+// be inferred to `2` requiring `NotCopy: Copy`, and in the repeat expr case it would
+// be inferred to `1`.
+
+use std::marker::PhantomData;
+
+struct NotCopy;
+
+struct Foo<T>(PhantomData<T>);
+
+impl Clone for Foo<u32> {
+    fn clone(&self) -> Self {
+        Foo(PhantomData)
+    }
+}
+
+impl Copy for Foo<u32> {}
+
+fn tie<T>(_: &T, _: [Foo<T>; 2]) {}
+
+trait Trait<const N: usize> {}
+
+impl Trait<2> for i32 {}
+impl Trait<1> for u32 {}
+
+fn make_goal<T: Trait<N>, const N: usize>(_: &T, _: [NotCopy; N]) {}
+
+fn main() {
+    let a = 1;
+    let b: [Foo<_>; 2] = [Foo(PhantomData); _];
+    tie(&a, b);
+    let c = [NotCopy; _];
+
+    // a is of type `?y`
+    // b is of type `[Foo<?y>; 2]`
+    // c is of type `[NotCopy; ?x]`
+    // there is a goal ?y: Trait<?x>` with two candidates:
+    // - `i32: Trait<2>`, ?y=i32 ?x=2 which requires `NotCopy: Copy` when expr checks happen
+    // - `u32: Trait<1>` ?y=u32 ?x=1 which doesnt require `NotCopy: Copy`
+    make_goal(&a, c);
+
+    // final repeat expr checks:
+    //
+    // `Foo<?y>; 2`
+    // - Foo<?y>: Copy
+    // - requires ?y=u32
+    //
+    // `NotCopy; ?x`
+    // - fails if fallback happens before repeat exprs as `i32: Trait<?x>` infers `?x=2`
+    // - succeeds if repeat expr checks happen first as `?y=u32` means `u32: Trait<?x>`
+    //    infers `?x=1`
+}
diff --git a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.gai.stderr b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.gai.stderr
new file mode 100644
index 0000000000000..de38476c82b30
--- /dev/null
+++ b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.gai.stderr
@@ -0,0 +1,17 @@
+error[E0282]: type annotations needed for `[Foo<_>; 2]`
+  --> $DIR/copy-inference-side-effects-are-lazy.rs:22:9
+   |
+LL |     let x = [Foo(PhantomData); 2];
+   |         ^
+LL |
+LL |     _ = extract(x).max(2);
+   |         ---------- type must be known at this point
+   |
+help: consider giving `x` an explicit type, where the type for type parameter `T` is specified
+   |
+LL |     let x: [Foo<T>; 2] = [Foo(PhantomData); 2];
+   |          +++++++++++++
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.rs b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.rs
new file mode 100644
index 0000000000000..0b0672d9c2b56
--- /dev/null
+++ b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.rs
@@ -0,0 +1,25 @@
+//@revisions: current gai
+//@[current] check-pass
+
+#![cfg_attr(gai, feature(generic_arg_infer))]
+
+use std::marker::PhantomData;
+
+struct Foo<T>(PhantomData<T>);
+
+impl Clone for Foo<u8> {
+    fn clone(&self) -> Self {
+        Foo(PhantomData)
+    }
+}
+impl Copy for Foo<u8> {}
+
+fn extract<T, const N: usize>(_: [Foo<T>; N]) -> T {
+    loop {}
+}
+
+fn main() {
+    let x = [Foo(PhantomData); 2];
+    //[gai]~^ ERROR: type annotations needed
+    _ = extract(x).max(2);
+}
diff --git a/tests/ui/repeat-expr/infer.rs b/tests/ui/repeat-expr/copy-inference-side-effects.rs
similarity index 100%
rename from tests/ui/repeat-expr/infer.rs
rename to tests/ui/repeat-expr/copy-inference-side-effects.rs
diff --git a/tests/ui/repeat-expr/dont-require-copy-on-infer.rs b/tests/ui/repeat-expr/dont-require-copy-on-infer.rs
new file mode 100644
index 0000000000000..e81bf1595be1f
--- /dev/null
+++ b/tests/ui/repeat-expr/dont-require-copy-on-infer.rs
@@ -0,0 +1,6 @@
+//@ check-pass
+#![feature(generic_arg_infer)]
+
+fn main() {
+    let a: [_; 1] = [String::new(); _];
+}
diff --git a/tests/ui/repeat-expr/no-conservative-copy-impl-requirement.rs b/tests/ui/repeat-expr/no-conservative-copy-impl-requirement.rs
new file mode 100644
index 0000000000000..eb70df62996fb
--- /dev/null
+++ b/tests/ui/repeat-expr/no-conservative-copy-impl-requirement.rs
@@ -0,0 +1,20 @@
+#![feature(generic_arg_infer)]
+
+struct Foo<const N: usize>;
+
+impl Clone for Foo<1> {
+    fn clone(&self) -> Self {
+        Foo
+    }
+}
+impl Copy for Foo<1> {}
+
+fn unify<const N: usize>(_: &[Foo<N>; N]) {
+    loop {}
+}
+
+fn main() {
+    let x = &[Foo::<_>; _];
+    //~^ ERROR: type annotations needed for `&[Foo<_>; _]`
+    _ = unify(x);
+}
diff --git a/tests/ui/repeat-expr/no-conservative-copy-impl-requirement.stderr b/tests/ui/repeat-expr/no-conservative-copy-impl-requirement.stderr
new file mode 100644
index 0000000000000..04f8ff33fdab3
--- /dev/null
+++ b/tests/ui/repeat-expr/no-conservative-copy-impl-requirement.stderr
@@ -0,0 +1,14 @@
+error[E0282]: type annotations needed for `&[Foo<_>; _]`
+  --> $DIR/no-conservative-copy-impl-requirement.rs:17:9
+   |
+LL |     let x = &[Foo::<_>; _];
+   |         ^     -------- type must be known at this point
+   |
+help: consider giving `x` an explicit type, where the value of const parameter `N` is specified
+   |
+LL |     let x: &[Foo<N>; N] = &[Foo::<_>; _];
+   |          ++++++++++++++
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/suggestions/abi-typo.stderr b/tests/ui/suggestions/abi-typo.stderr
index 5195c43220b41..4d89ac16570ed 100644
--- a/tests/ui/suggestions/abi-typo.stderr
+++ b/tests/ui/suggestions/abi-typo.stderr
@@ -2,12 +2,14 @@ error[E0703]: invalid ABI: found `cdedl`
   --> $DIR/abi-typo.rs:2:8
    |
 LL | extern "cdedl" fn cdedl() {}
-   |        ^^^^^^^
-   |        |
-   |        invalid ABI
-   |        help: did you mean: `"cdecl"`
+   |        ^^^^^^^ invalid ABI
    |
    = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions
+help: there's a similarly named valid ABI `cdecl`
+   |
+LL - extern "cdedl" fn cdedl() {}
+LL + extern "cdecl" fn cdedl() {}
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.rs b/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.rs
new file mode 100644
index 0000000000000..2ebbed3c740f4
--- /dev/null
+++ b/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.rs
@@ -0,0 +1,11 @@
+struct T(String);
+
+impl PartialEq<String> for T {
+    fn eq(&self, other: &String) -> bool {
+        &self.0 == other
+    }
+}
+
+fn main() {
+    String::from("Girls Band Cry") == T(String::from("Girls Band Cry")); //~ can't compare `String` with `T` [E0277]
+}
diff --git a/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr b/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr
new file mode 100644
index 0000000000000..ebe103ef19a1c
--- /dev/null
+++ b/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr
@@ -0,0 +1,24 @@
+error[E0277]: can't compare `String` with `T`
+  --> $DIR/partialeq_suggest_swap_on_e0277.rs:10:36
+   |
+LL |     String::from("Girls Band Cry") == T(String::from("Girls Band Cry"));
+   |                                    ^^ no implementation for `String == T`
+   |
+   = help: the trait `PartialEq<T>` is not implemented for `String`
+   = help: the following other types implement trait `PartialEq<Rhs>`:
+             `String` implements `PartialEq<&str>`
+             `String` implements `PartialEq<ByteStr>`
+             `String` implements `PartialEq<ByteString>`
+             `String` implements `PartialEq<Cow<'_, str>>`
+             `String` implements `PartialEq<str>`
+             `String` implements `PartialEq`
+   = note: `T` implements `PartialEq<String>`
+help: consider swapping the equality
+   |
+LL -     String::from("Girls Band Cry") == T(String::from("Girls Band Cry"));
+LL +     T(String::from("Girls Band Cry")) == String::from("Girls Band Cry");
+   |
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.