about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorThe Miri Cronjob Bot <miri@cron.bot>2024-07-27 05:22:40 +0000
committerThe Miri Cronjob Bot <miri@cron.bot>2024-07-27 05:22:40 +0000
commit00e89d3cb6c549d195c41557ee467ddc559b3efe (patch)
tree1facc011e12f747c51718d5acb5dd4f0f9719ed6 /compiler
parent80a32f86187f897cc075a314cd689c6bd45c4671 (diff)
parenta526d7ce45fd2284e0e7c7556ccba2425b9d25e5 (diff)
downloadrust-00e89d3cb6c549d195c41557ee467ddc559b3efe.tar.gz
rust-00e89d3cb6c549d195c41557ee467ddc559b3efe.zip
Merge from rustc
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs16
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs34
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs28
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs5
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_hir/src/hir.rs6
-rw-r--r--compiler/rustc_hir/src/intravisit.rs4
-rw-r--r--compiler/rustc_hir/src/lang_items.rs1
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl13
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs31
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs27
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs135
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs77
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs9
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs5
-rw-r--r--compiler/rustc_lint/messages.ftl19
-rw-r--r--compiler/rustc_lint/src/builtin.rs139
-rw-r--r--compiler/rustc_lint/src/lints.rs141
-rw-r--r--compiler/rustc_lint/src/non_local_def.rs4
-rw-r--r--compiler/rustc_lint/src/traits.rs6
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs6
-rw-r--r--compiler/rustc_mir_transform/src/instsimplify.rs4
-rw-r--r--compiler/rustc_mir_transform/src/shim.rs6
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs5
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs11
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs123
-rw-r--r--compiler/rustc_parse/src/parser/item.rs9
-rw-r--r--compiler/rustc_passes/src/dead.rs32
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs5
-rw-r--r--compiler/rustc_resolve/src/errors.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs17
-rw-r--r--compiler/rustc_type_ir/src/visit.rs4
40 files changed, 558 insertions, 388 deletions
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index f990b4ba69b..6e6aac1ddfc 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -172,7 +172,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         id: NodeId,
         hir_id: hir::HirId,
         ident: &mut Ident,
-        attrs: Option<&'hir [Attribute]>,
+        attrs: &'hir [Attribute],
         vis_span: Span,
         i: &ItemKind,
     ) -> hir::ItemKind<'hir> {
@@ -488,7 +488,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         id: NodeId,
         vis_span: Span,
         ident: &mut Ident,
-        attrs: Option<&'hir [Attribute]>,
+        attrs: &'hir [Attribute],
     ) -> hir::ItemKind<'hir> {
         let path = &tree.prefix;
         let segments = prefix.segments.iter().chain(path.segments.iter()).cloned().collect();
@@ -566,7 +566,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         // `ItemLocalId` and the new owner. (See `lower_node_id`)
                         let kind =
                             this.lower_use_tree(use_tree, &prefix, id, vis_span, &mut ident, attrs);
-                        if let Some(attrs) = attrs {
+                        if !attrs.is_empty() {
                             this.attrs.insert(hir::ItemLocalId::ZERO, attrs);
                         }
 
@@ -1525,8 +1525,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     continue;
                 }
                 let is_param = *is_param.get_or_insert_with(compute_is_param);
-                if !is_param {
-                    self.dcx().emit_err(MisplacedRelaxTraitBound { span: bound.span() });
+                if !is_param && !self.tcx.features().more_maybe_bounds {
+                    self.tcx
+                        .sess
+                        .create_feature_err(
+                            MisplacedRelaxTraitBound { span: bound.span() },
+                            sym::more_maybe_bounds,
+                        )
+                        .emit();
                 }
             }
         }
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 0f5f4d8023b..d44f953010a 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -913,15 +913,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         ret
     }
 
-    fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> Option<&'hir [Attribute]> {
+    fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> &'hir [Attribute] {
         if attrs.is_empty() {
-            None
+            &[]
         } else {
             debug_assert_eq!(id.owner, self.current_hir_id_owner);
             let ret = self.arena.alloc_from_iter(attrs.iter().map(|a| self.lower_attr(a)));
             debug_assert!(!ret.is_empty());
             self.attrs.insert(id.local_id, ret);
-            Some(ret)
+            ret
         }
     }
 
@@ -1216,6 +1216,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     itctx,
                     TraitBoundModifiers::NONE,
                 );
+                let bound = (bound, hir::TraitBoundModifier::None);
                 let bounds = this.arena.alloc_from_iter([bound]);
                 let lifetime_bound = this.elided_dyn_bound(t.span);
                 (bounds, lifetime_bound)
@@ -1348,21 +1349,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                             // We can safely ignore constness here since AST validation
                             // takes care of rejecting invalid modifier combinations and
                             // const trait bounds in trait object types.
-                            GenericBound::Trait(ty, modifiers) => match modifiers.polarity {
-                                BoundPolarity::Positive | BoundPolarity::Negative(_) => {
-                                    Some(this.lower_poly_trait_ref(
-                                        ty,
-                                        itctx,
-                                        // Still, don't pass along the constness here; we don't want to
-                                        // synthesize any host effect args, it'd only cause problems.
-                                        TraitBoundModifiers {
-                                            constness: BoundConstness::Never,
-                                            ..*modifiers
-                                        },
-                                    ))
-                                }
-                                BoundPolarity::Maybe(_) => None,
-                            },
+                            GenericBound::Trait(ty, modifiers) => {
+                                // Still, don't pass along the constness here; we don't want to
+                                // synthesize any host effect args, it'd only cause problems.
+                                let modifiers = TraitBoundModifiers {
+                                    constness: BoundConstness::Never,
+                                    ..*modifiers
+                                };
+                                let trait_ref = this.lower_poly_trait_ref(ty, itctx, modifiers);
+                                let polarity = this.lower_trait_bound_modifiers(modifiers);
+                                Some((trait_ref, polarity))
+                            }
                             GenericBound::Outlives(lifetime) => {
                                 if lifetime_bound.is_none() {
                                     lifetime_bound = Some(this.lower_lifetime(lifetime));
@@ -2688,6 +2685,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                             trait_ref: hir::TraitRef { path, hir_ref_id: hir_id },
                             span: self.lower_span(span),
                         };
+                        let principal = (principal, hir::TraitBoundModifier::None);
 
                         // The original ID is taken by the `PolyTraitRef`,
                         // so the `Ty` itself needs a different one.
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 1088db74cc9..9b063a330b7 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -1345,14 +1345,28 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
         match bound {
             GenericBound::Trait(trait_ref, modifiers) => {
                 match (ctxt, modifiers.constness, modifiers.polarity) {
-                    (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) => {
-                        self.dcx().emit_err(errors::OptionalTraitSupertrait {
-                            span: trait_ref.span,
-                            path_str: pprust::path_to_string(&trait_ref.trait_ref.path),
-                        });
+                    (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_))
+                        if !self.features.more_maybe_bounds =>
+                    {
+                        self.session
+                            .create_feature_err(
+                                errors::OptionalTraitSupertrait {
+                                    span: trait_ref.span,
+                                    path_str: pprust::path_to_string(&trait_ref.trait_ref.path),
+                                },
+                                sym::more_maybe_bounds,
+                            )
+                            .emit();
                     }
-                    (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) => {
-                        self.dcx().emit_err(errors::OptionalTraitObject { span: trait_ref.span });
+                    (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_))
+                        if !self.features.more_maybe_bounds =>
+                    {
+                        self.session
+                            .create_feature_err(
+                                errors::OptionalTraitObject { span: trait_ref.span },
+                                sym::more_maybe_bounds,
+                            )
+                            .emit();
                     }
                     (
                         BoundKind::TraitObject,
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 2d9bc45ebc8..e5bc4b38748 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -1442,9 +1442,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
             // See `tests/ui/moves/needs-clone-through-deref.rs`
             return false;
         }
+        // We don't want to suggest `.clone()` in a move closure, since the value has already been captured.
         if self.in_move_closure(expr) {
             return false;
         }
+        // We also don't want to suggest cloning a closure itself, since the value has already been captured.
+        if let hir::ExprKind::Closure(_) = expr.kind {
+            return false;
+        }
         // Try to find predicates on *generic params* that would allow copying `ty`
         let mut suggestion =
             if let Some(symbol) = tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 9b5ed3b0876..d3d07181096 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -205,6 +205,8 @@ declare_features! (
     (unstable, lifetime_capture_rules_2024, "1.76.0", None),
     /// Allows `#[link(..., cfg(..))]`; perma-unstable per #37406
     (unstable, link_cfg, "1.14.0", None),
+    /// Allows using `?Trait` trait bounds in more contexts.
+    (internal, more_maybe_bounds, "CURRENT_RUSTC_VERSION", None),
     /// Allows the `multiple_supertrait_upcastable` lint.
     (unstable, multiple_supertrait_upcastable, "1.69.0", None),
     /// Allow negative trait bounds. This is an internal-only feature for testing the trait solver!
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 20b15499234..8c8f760bc41 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -2832,7 +2832,11 @@ pub enum TyKind<'hir> {
     OpaqueDef(ItemId, &'hir [GenericArg<'hir>], bool),
     /// A trait object type `Bound1 + Bound2 + Bound3`
     /// where `Bound` is a trait or a lifetime.
-    TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax),
+    TraitObject(
+        &'hir [(PolyTraitRef<'hir>, TraitBoundModifier)],
+        &'hir Lifetime,
+        TraitObjectSyntax,
+    ),
     /// Unused for now.
     Typeof(&'hir AnonConst),
     /// `TyKind::Infer` means the type should be inferred instead of it having been
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index c202ee41e31..696f548f1ba 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -902,7 +902,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul
             try_visit!(visitor.visit_array_length(length));
         }
         TyKind::TraitObject(bounds, ref lifetime, _syntax) => {
-            walk_list!(visitor, visit_poly_trait_ref, bounds);
+            for (bound, _modifier) in bounds {
+                try_visit!(visitor.visit_poly_trait_ref(bound));
+            }
             try_visit!(visitor.visit_lifetime(lifetime));
         }
         TyKind::Typeof(ref expression) => try_visit!(visitor.visit_anon_const(expression)),
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 58cc0f62111..1821387e85f 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -162,6 +162,7 @@ language_item_table! {
     StructuralPeq,           sym::structural_peq,      structural_peq_trait,       Target::Trait,          GenericRequirement::None;
     Copy,                    sym::copy,                copy_trait,                 Target::Trait,          GenericRequirement::Exact(0);
     Clone,                   sym::clone,               clone_trait,                Target::Trait,          GenericRequirement::None;
+    CloneFn,                 sym::clone_fn,            clone_fn,                   Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
     Sync,                    sym::sync,                sync_trait,                 Target::Trait,          GenericRequirement::Exact(0);
     DiscriminantKind,        sym::discriminant_kind,   discriminant_kind_trait,    Target::Trait,          GenericRequirement::None;
     /// The associated item of the `DiscriminantKind` trait.
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index cc404daa51f..367f6e17e7f 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -1,4 +1,4 @@
-hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$ty_param_name}`
+hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$qself}`
     .label = ambiguous associated {$assoc_kind} `{$assoc_name}`
 
 hir_analysis_ambiguous_lifetime_bound =
@@ -12,16 +12,21 @@ hir_analysis_assoc_item_is_private = {$kind} `{$name}` is private
     .label = private {$kind}
     .defined_here_label = the {$kind} is defined here
 
-hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$ty_param_name}`
+hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$qself}`
 
 hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identically_named ->
         [true] an
         *[false] a similarly named
     } associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}`
 hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found
-hir_analysis_assoc_item_not_found_other_sugg = `{$ty_param_name}` has the following associated {$assoc_kind}
+hir_analysis_assoc_item_not_found_other_sugg = `{$qself}` has the following associated {$assoc_kind}
+hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg =
+    consider fully qualifying{$identically_named ->
+        [true] {""}
+        *[false] {" "}and renaming
+    } the associated {$assoc_kind}
 hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg = change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}`
-hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = and also change the associated {$assoc_kind} name
+hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = ...and changing the associated {$assoc_kind} name
 hir_analysis_assoc_item_not_found_similar_sugg = there is an associated {$assoc_kind} with a similar name
 
 hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got}
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 0316ef69bf8..456dc4e4e07 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -831,7 +831,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GATArgsCollector<'tcx> {
 
 fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool {
     match ty.kind {
-        hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments {
+        hir::TyKind::TraitObject([(trait_ref, _)], ..) => match trait_ref.trait_ref.path.segments {
             [s] => s.res.opt_def_id() == Some(trait_def_id.to_def_id()),
             _ => false,
         },
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 349dc9ad00e..02ea95852f0 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -652,7 +652,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                 debug!(?bounds, ?lifetime, "TraitObject");
                 let scope = Scope::TraitRefBoundary { s: self.scope };
                 self.with(scope, |this| {
-                    for bound in bounds {
+                    for (bound, _) in bounds {
                         this.visit_poly_trait_ref_inner(
                             bound,
                             NonLifetimeBinderAllowed::Deny("trait object types"),
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index c83788928a9..c364a561631 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -22,7 +22,7 @@ pub struct AmbiguousAssocItem<'a> {
     pub span: Span,
     pub assoc_kind: &'static str,
     pub assoc_name: Ident,
-    pub ty_param_name: &'a str,
+    pub qself: &'a str,
 }
 
 #[derive(Diagnostic)]
@@ -75,7 +75,7 @@ pub struct AssocItemNotFound<'a> {
     pub span: Span,
     pub assoc_name: Ident,
     pub assoc_kind: &'static str,
-    pub ty_param_name: &'a str,
+    pub qself: &'a str,
     #[subdiagnostic]
     pub label: Option<AssocItemNotFoundLabel<'a>>,
     #[subdiagnostic]
@@ -126,13 +126,32 @@ pub enum AssocItemNotFoundSugg<'a> {
         assoc_kind: &'static str,
         suggested_name: Symbol,
     },
-    #[suggestion(hir_analysis_assoc_item_not_found_other_sugg, code = "{suggested_name}")]
+    #[multipart_suggestion(
+        hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg,
+        style = "verbose"
+    )]
+    SimilarInOtherTraitQPath {
+        #[suggestion_part(code = "<")]
+        lo: Span,
+        #[suggestion_part(code = " as {trait_ref}>")]
+        mi: Span,
+        #[suggestion_part(code = "{suggested_name}")]
+        hi: Option<Span>,
+        trait_ref: String,
+        suggested_name: Symbol,
+        identically_named: bool,
+        #[applicability]
+        applicability: Applicability,
+    },
+    #[suggestion(
+        hir_analysis_assoc_item_not_found_other_sugg,
+        code = "{suggested_name}",
+        applicability = "maybe-incorrect"
+    )]
     Other {
         #[primary_span]
         span: Span,
-        #[applicability]
-        applicability: Applicability,
-        ty_param_name: &'a str,
+        qself: &'a str,
         assoc_kind: &'static str,
         suggested_name: Symbol,
     },
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 6f9c481650b..30c04aa47a3 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -6,19 +6,17 @@ use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::bug;
-use rustc_middle::ty::print::PrintTraitRefExt as _;
 use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt};
 use rustc_span::symbol::Ident;
-use rustc_span::{ErrorGuaranteed, Span, Symbol};
+use rustc_span::{sym, ErrorGuaranteed, Span, Symbol};
 use rustc_trait_selection::traits;
 use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
 use smallvec::SmallVec;
 
 use crate::bounds::Bounds;
 use crate::errors;
-use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter};
-
-use super::RegionInferReason;
+use crate::hir_ty_lowering::HirTyLowerer;
+use crate::hir_ty_lowering::{AssocItemQSelf, OnlySelfBounds, PredicateFilter, RegionInferReason};
 
 impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     /// Add a `Sized` bound to the `bounds` if appropriate.
@@ -75,10 +73,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             }
         }
 
+        let mut unique_bounds = FxIndexSet::default();
+        let mut seen_repeat = false;
+        for unbound in &unbounds {
+            if let Res::Def(DefKind::Trait, unbound_def_id) = unbound.trait_ref.path.res {
+                seen_repeat |= !unique_bounds.insert(unbound_def_id);
+            }
+        }
         if unbounds.len() > 1 {
-            self.dcx().emit_err(errors::MultipleRelaxedDefaultBounds {
+            let err = errors::MultipleRelaxedDefaultBounds {
                 spans: unbounds.iter().map(|ptr| ptr.span).collect(),
-            });
+            };
+            if seen_repeat {
+                self.dcx().emit_err(err);
+            } else if !tcx.features().more_maybe_bounds {
+                self.tcx().sess.create_feature_err(err, sym::more_maybe_bounds).emit();
+            };
         }
 
         let mut seen_sized_unbound = false;
@@ -288,8 +298,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             // one that does define it.
             self.probe_single_bound_for_assoc_item(
                 || traits::supertraits(tcx, trait_ref),
-                trait_ref.skip_binder().print_only_trait_name(),
-                None,
+                AssocItemQSelf::Trait(trait_ref.def_id()),
                 assoc_kind,
                 constraint.ident,
                 path_span,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index 8ff6ced8b39..20f06d77489 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -3,7 +3,7 @@ use crate::errors::{
     ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits,
 };
 use crate::fluent_generated as fluent;
-use crate::hir_ty_lowering::HirTyLowerer;
+use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer};
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::unord::UnordMap;
@@ -11,9 +11,9 @@ use rustc_errors::MultiSpan;
 use rustc_errors::{
     codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed,
 };
+use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::{self as hir, Node};
+use rustc_hir::def_id::DefId;
 use rustc_middle::bug;
 use rustc_middle::query::Key;
 use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _};
@@ -116,8 +116,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     pub(super) fn complain_about_assoc_item_not_found<I>(
         &self,
         all_candidates: impl Fn() -> I,
-        ty_param_name: &str,
-        ty_param_def_id: Option<LocalDefId>,
+        qself: AssocItemQSelf,
         assoc_kind: ty::AssocKind,
         assoc_name: Ident,
         span: Span,
@@ -139,7 +138,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             );
         }
 
-        let assoc_kind_str = super::assoc_kind_str(assoc_kind);
+        let assoc_kind_str = assoc_kind_str(assoc_kind);
+        let qself_str = qself.to_string(tcx);
 
         // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a
         // valid span, so we point at the whole path segment instead.
@@ -149,7 +149,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             span: if is_dummy { span } else { assoc_name.span },
             assoc_name,
             assoc_kind: assoc_kind_str,
-            ty_param_name,
+            qself: &qself_str,
             label: None,
             sugg: None,
         };
@@ -219,19 +219,28 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     suggested_name,
                     identically_named: suggested_name == assoc_name.name,
                 });
-                let hir = tcx.hir();
-                if let Some(def_id) = ty_param_def_id
-                    && let parent = hir.get_parent_item(tcx.local_def_id_to_hir_id(def_id))
-                    && let Some(generics) = hir.get_generics(parent.def_id)
+                if let AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span) = qself
+                    // Not using `self.item_def_id()` here as that would yield the opaque type itself if we're
+                    // inside an opaque type while we're interested in the overarching type alias (TAIT).
+                    // FIXME: However, for trait aliases, this incorrectly returns the enclosing module...
+                    && let item_def_id =
+                        tcx.hir().get_parent_item(tcx.local_def_id_to_hir_id(ty_param_def_id))
+                    // FIXME: ...which obviously won't have any generics.
+                    && let Some(generics) = tcx.hir().get_generics(item_def_id.def_id)
                 {
-                    if generics.bounds_for_param(def_id).flat_map(|pred| pred.bounds.iter()).any(
-                        |b| match b {
+                    // FIXME: Suggest adding supertrait bounds if we have a `Self` type param.
+                    // FIXME(trait_alias): Suggest adding `Self: Trait` to
+                    // `trait Alias = where Self::Proj:;` with `trait Trait { type Proj; }`.
+                    if generics
+                        .bounds_for_param(ty_param_def_id)
+                        .flat_map(|pred| pred.bounds.iter())
+                        .any(|b| match b {
                             hir::GenericBound::Trait(t, ..) => {
                                 t.trait_ref.trait_def_id() == Some(best_trait)
                             }
                             _ => false,
-                        },
-                    ) {
+                        })
+                    {
                         // The type param already has a bound for `trait_name`, we just need to
                         // change the associated item.
                         err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait {
@@ -242,48 +251,60 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         return self.dcx().emit_err(err);
                     }
 
-                    let mut err = self.dcx().create_err(err);
-                    if suggest_constraining_type_param(
-                        tcx,
-                        generics,
-                        &mut err,
-                        &ty_param_name,
-                        &trait_name,
-                        None,
-                        None,
-                    ) && suggested_name != assoc_name.name
+                    let trait_args = &ty::GenericArgs::identity_for_item(tcx, best_trait)[1..];
+                    let mut trait_ref = trait_name.clone();
+                    let applicability = if let [arg, args @ ..] = trait_args {
+                        use std::fmt::Write;
+                        write!(trait_ref, "</* {arg}").unwrap();
+                        args.iter().try_for_each(|arg| write!(trait_ref, ", {arg}")).unwrap();
+                        trait_ref += " */>";
+                        Applicability::HasPlaceholders
+                    } else {
+                        Applicability::MaybeIncorrect
+                    };
+
+                    let identically_named = suggested_name == assoc_name.name;
+
+                    if let DefKind::TyAlias = tcx.def_kind(item_def_id)
+                        && !tcx.type_alias_is_lazy(item_def_id)
                     {
-                        // We suggested constraining a type parameter, but the associated item on it
-                        // was also not an exact match, so we also suggest changing it.
-                        err.span_suggestion_verbose(
-                            assoc_name.span,
-                            fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg,
+                        err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTraitQPath {
+                            lo: ty_param_span.shrink_to_lo(),
+                            mi: ty_param_span.shrink_to_hi(),
+                            hi: (!identically_named).then_some(assoc_name.span),
+                            trait_ref,
+                            identically_named,
                             suggested_name,
-                            Applicability::MaybeIncorrect,
-                        );
+                            applicability,
+                        });
+                    } else {
+                        let mut err = self.dcx().create_err(err);
+                        if suggest_constraining_type_param(
+                            tcx, generics, &mut err, &qself_str, &trait_ref, None, None,
+                        ) && !identically_named
+                        {
+                            // We suggested constraining a type parameter, but the associated item on it
+                            // was also not an exact match, so we also suggest changing it.
+                            err.span_suggestion_verbose(
+                                assoc_name.span,
+                                fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg,
+                                suggested_name,
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
+                        return err.emit();
                     }
-                    return err.emit();
                 }
                 return self.dcx().emit_err(err);
             }
         }
 
         // If we still couldn't find any associated item, and only one associated item exists,
-        // suggests using it.
+        // suggest using it.
         if let [candidate_name] = all_candidate_names.as_slice() {
-            // This should still compile, except on `#![feature(associated_type_defaults)]`
-            // where it could suggests `type A = Self::A`, thus recursing infinitely.
-            let applicability =
-                if assoc_kind == ty::AssocKind::Type && tcx.features().associated_type_defaults {
-                    Applicability::Unspecified
-                } else {
-                    Applicability::MaybeIncorrect
-                };
-
             err.sugg = Some(errors::AssocItemNotFoundSugg::Other {
                 span: assoc_name.span,
-                applicability,
-                ty_param_name,
+                qself: &qself_str,
                 assoc_kind: assoc_kind_str,
                 suggested_name: *candidate_name,
             });
@@ -349,10 +370,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
         self.dcx().emit_err(errors::AssocKindMismatch {
             span,
-            expected: super::assoc_kind_str(expected),
-            got: super::assoc_kind_str(got),
+            expected: assoc_kind_str(expected),
+            got: assoc_kind_str(got),
             expected_because_label,
-            assoc_kind: super::assoc_kind_str(assoc_item.kind),
+            assoc_kind: assoc_kind_str(assoc_item.kind),
             def_span: tcx.def_span(assoc_item.def_id),
             bound_on_assoc_const_label,
             wrap_in_braces_sugg,
@@ -698,7 +719,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         &self,
         associated_types: FxIndexMap<Span, FxIndexSet<DefId>>,
         potential_assoc_types: Vec<usize>,
-        trait_bounds: &[hir::PolyTraitRef<'_>],
+        trait_bounds: &[(hir::PolyTraitRef<'_>, hir::TraitBoundModifier)],
     ) {
         if associated_types.values().all(|v| v.is_empty()) {
             return;
@@ -744,12 +765,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         // related to issue #91997, turbofishes added only when in an expr or pat
         let mut in_expr_or_pat = false;
         if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) {
-            let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id));
+            let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.0.trait_ref.hir_ref_id));
             in_expr_or_pat = match grandparent {
-                Node::Expr(_) | Node::Pat(_) => true,
+                hir::Node::Expr(_) | hir::Node::Pat(_) => true,
                 _ => false,
             };
-            match bound.trait_ref.path.segments {
+            match bound.0.trait_ref.path.segments {
                 // FIXME: `trait_ref.path.span` can point to a full path with multiple
                 // segments, even though `trait_ref.path.segments` is of length `1`. Work
                 // around that bug here, even though it should be fixed elsewhere.
@@ -790,7 +811,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         // and we can then use their span to indicate this to the user.
         let bound_names = trait_bounds
             .iter()
-            .filter_map(|poly_trait_ref| {
+            .filter_map(|(poly_trait_ref, _)| {
                 let path = poly_trait_ref.trait_ref.path.segments.last()?;
                 let args = path.args?;
 
@@ -1612,3 +1633,11 @@ fn generics_args_err_extend<'a>(
         _ => {}
     }
 }
+
+pub(super) fn assoc_kind_str(kind: ty::AssocKind) -> &'static str {
+    match kind {
+        ty::AssocKind::Fn => "function",
+        ty::AssocKind::Const => "constant",
+        ty::AssocKind::Type => "type",
+    }
+}
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
index 29c71c3fa50..e7aad0a29c5 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
@@ -34,7 +34,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 .ok()
                 .is_some_and(|s| s.trim_end().ends_with('<'));
 
-        let is_global = poly_trait_ref.trait_ref.path.is_global();
+        let is_global = poly_trait_ref.0.trait_ref.path.is_global();
 
         let mut sugg = vec![(
             self_ty.span.shrink_to_lo(),
@@ -176,7 +176,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let mut is_downgradable = true;
         let is_object_safe = match self_ty.kind {
             hir::TyKind::TraitObject(objects, ..) => {
-                objects.iter().all(|o| match o.trait_ref.path.res {
+                objects.iter().all(|(o, _)| match o.trait_ref.path.res {
                     Res::Def(DefKind::Trait, id) => {
                         if Some(id) == owner {
                             // For recursive traits, don't downgrade the error. (#119652)
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index d6eb1a66902..ce298641e60 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -55,7 +55,6 @@ use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::wf::object_region_bounds;
 use rustc_trait_selection::traits::{self, ObligationCtxt};
 
-use std::fmt::Display;
 use std::slice;
 
 /// A path segment that is semantically allowed to have generic arguments.
@@ -193,6 +192,25 @@ pub trait HirTyLowerer<'tcx> {
     }
 }
 
+/// The "qualified self" of an associated item path.
+///
+/// For diagnostic purposes only.
+enum AssocItemQSelf {
+    Trait(DefId),
+    TyParam(LocalDefId, Span),
+    SelfTyAlias,
+}
+
+impl AssocItemQSelf {
+    fn to_string(&self, tcx: TyCtxt<'_>) -> String {
+        match *self {
+            Self::Trait(def_id) => tcx.def_path_str(def_id),
+            Self::TyParam(def_id, _) => tcx.hir().ty_param_name(def_id).to_string(),
+            Self::SelfTyAlias => kw::SelfUpper.to_string(),
+        }
+    }
+}
+
 /// New-typed boolean indicating whether explicit late-bound lifetimes
 /// are present in a set of generic arguments.
 ///
@@ -802,6 +820,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     fn probe_single_ty_param_bound_for_assoc_ty(
         &self,
         ty_param_def_id: LocalDefId,
+        ty_param_span: Span,
         assoc_name: Ident,
         span: Span,
     ) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> {
@@ -811,19 +830,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let predicates = &self.probe_ty_param_bounds(span, ty_param_def_id, assoc_name).predicates;
         debug!("predicates={:#?}", predicates);
 
-        let param_name = tcx.hir().ty_param_name(ty_param_def_id);
         self.probe_single_bound_for_assoc_item(
             || {
-                traits::transitive_bounds_that_define_assoc_item(
-                    tcx,
-                    predicates
-                        .iter()
-                        .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))),
-                    assoc_name,
-                )
+                let trait_refs = predicates
+                    .iter()
+                    .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref)));
+                traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name)
             },
-            param_name,
-            Some(ty_param_def_id),
+            AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span),
             ty::AssocKind::Type,
             assoc_name,
             span,
@@ -835,12 +849,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     ///
     /// This fails if there is no such bound in the list of candidates or if there are multiple
     /// candidates in which case it reports ambiguity.
-    #[instrument(level = "debug", skip(self, all_candidates, ty_param_name, constraint), ret)]
+    #[instrument(level = "debug", skip(self, all_candidates, qself, constraint), ret)]
     fn probe_single_bound_for_assoc_item<I>(
         &self,
         all_candidates: impl Fn() -> I,
-        ty_param_name: impl Display,
-        ty_param_def_id: Option<LocalDefId>,
+        qself: AssocItemQSelf,
         assoc_kind: ty::AssocKind,
         assoc_name: Ident,
         span: Span,
@@ -858,8 +871,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let Some(bound) = matching_candidates.next() else {
             let reported = self.complain_about_assoc_item_not_found(
                 all_candidates,
-                &ty_param_name.to_string(),
-                ty_param_def_id,
+                qself,
                 assoc_kind,
                 assoc_name,
                 span,
@@ -872,13 +884,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         if let Some(bound2) = matching_candidates.next() {
             debug!(?bound2);
 
-            let assoc_kind_str = assoc_kind_str(assoc_kind);
-            let ty_param_name = &ty_param_name.to_string();
+            let assoc_kind_str = errors::assoc_kind_str(assoc_kind);
+            let qself_str = qself.to_string(tcx);
             let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem {
                 span,
                 assoc_kind: assoc_kind_str,
                 assoc_name,
-                ty_param_name,
+                qself: &qself_str,
             });
             // Provide a more specific error code index entry for equality bindings.
             err.code(
@@ -929,7 +941,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         err.span_suggestion_verbose(
                             span.with_hi(assoc_name.span.lo()),
                             "use fully-qualified syntax to disambiguate",
-                            format!("<{ty_param_name} as {}>::", bound.print_only_trait_path()),
+                            format!("<{qself_str} as {}>::", bound.print_only_trait_path()),
                             Applicability::MaybeIncorrect,
                         );
                     }
@@ -943,7 +955,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             if !where_bounds.is_empty() {
                 err.help(format!(
                     "consider introducing a new type parameter `T` and adding `where` constraints:\
-                     \n    where\n        T: {ty_param_name},\n{}",
+                     \n    where\n        T: {qself_str},\n{}",
                     where_bounds.join(",\n"),
                 ));
             }
@@ -997,11 +1009,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let tcx = self.tcx();
 
         let assoc_ident = assoc_segment.ident;
-        let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
-            path.res
-        } else {
-            Res::Err
-        };
 
         // Check if we have an enum variant or an inherent associated type.
         let mut variant_resolution = None;
@@ -1038,6 +1045,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             }
         }
 
+        let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
+            path.res
+        } else {
+            Res::Err
+        };
+
         // Find the type of the associated item, and the trait where the associated
         // item is declared.
         let bound = match (&qself_ty.kind(), qself_res) {
@@ -1056,8 +1069,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                             ty::Binder::dummy(trait_ref.instantiate_identity()),
                         )
                     },
-                    kw::SelfUpper,
-                    None,
+                    AssocItemQSelf::SelfTyAlias,
                     ty::AssocKind::Type,
                     assoc_ident,
                     span,
@@ -1069,6 +1081,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did),
             ) => self.probe_single_ty_param_bound_for_assoc_ty(
                 param_did.expect_local(),
+                qself.span,
                 assoc_ident,
                 span,
             )?,
@@ -2522,11 +2535,3 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         Some(r)
     }
 }
-
-fn assoc_kind_str(kind: ty::AssocKind) -> &'static str {
-    match kind {
-        ty::AssocKind::Fn => "function",
-        ty::AssocKind::Const => "constant",
-        ty::AssocKind::Type => "type",
-    }
-}
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
index aafadc7f9cb..b3c7a1ff8a8 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
@@ -27,7 +27,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         &self,
         span: Span,
         hir_id: hir::HirId,
-        hir_trait_bounds: &[hir::PolyTraitRef<'tcx>],
+        hir_trait_bounds: &[(hir::PolyTraitRef<'tcx>, hir::TraitBoundModifier)],
         lifetime: &hir::Lifetime,
         borrowed: bool,
         representation: DynKind,
@@ -37,7 +37,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let mut bounds = Bounds::default();
         let mut potential_assoc_types = Vec::new();
         let dummy_self = self.tcx().types.trait_object_dummy_self;
-        for trait_bound in hir_trait_bounds.iter().rev() {
+        for (trait_bound, modifier) in hir_trait_bounds.iter().rev() {
+            if *modifier == hir::TraitBoundModifier::Maybe {
+                continue;
+            }
             if let GenericArgCountResult {
                 correct:
                     Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }),
@@ -249,7 +252,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 let args = tcx.mk_args(&args);
 
                 let span = i.bottom().1;
-                let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| {
+                let empty_generic_args = hir_trait_bounds.iter().any(|(hir_bound, _)| {
                     hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
                         && hir_bound.span.contains(span)
                 });
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index a982b84e755..e25f43e169d 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -300,13 +300,16 @@ impl<'a> State<'a> {
                     self.word_space("dyn");
                 }
                 let mut first = true;
-                for bound in bounds {
+                for (bound, modifier) in bounds {
                     if first {
                         first = false;
                     } else {
                         self.nbsp();
                         self.word_space("+");
                     }
+                    if *modifier == TraitBoundModifier::Maybe {
+                        self.word("?");
+                    }
                     self.print_poly_trait_ref(bound);
                 }
                 if !lifetime.is_elided() {
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index a87b3c2c135..987dbf6db63 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -144,13 +144,18 @@ lint_builtin_special_module_name_used_main = found module declaration for main.r
 
 lint_builtin_trivial_bounds = {$predicate_kind_name} bound {$predicate} does not depend on any type or lifetime parameters
 
-lint_builtin_type_alias_bounds_help = use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to associated types in type aliases
-
-lint_builtin_type_alias_generic_bounds = bounds on generic parameters are not enforced in type aliases
-    .suggestion = the bound will not be checked when the type alias is used, and should be removed
-
-lint_builtin_type_alias_where_clause = where clauses are not enforced in type aliases
-    .suggestion = the clause will not be checked when the type alias is used, and should be removed
+lint_builtin_type_alias_bounds_enable_feat_help = add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
+lint_builtin_type_alias_bounds_label = will not be checked at usage sites of the type alias
+lint_builtin_type_alias_bounds_limitation_note = this is a known limitation of the type checker that may be lifted in a future edition.
+    see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+lint_builtin_type_alias_bounds_param_bounds = bounds on generic parameters in type aliases are not enforced
+    .suggestion = remove {$count ->
+        [one] this bound
+        *[other] these bounds
+    }
+lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg = fully qualify this associated type
+lint_builtin_type_alias_bounds_where_clause = where clauses on type aliases are not enforced
+    .suggestion = remove this where clause
 
 lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed
 lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 9ebada0fff3..ab0b47d48e5 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -31,12 +31,11 @@ use crate::{
         BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents,
         BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
         BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns,
-        BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds,
-        BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause,
+        BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds,
         BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit,
         BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe,
         BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
-        BuiltinWhileTrue, InvalidAsmLabel, SuggestChangingAssocTypes,
+        BuiltinWhileTrue, InvalidAsmLabel,
     },
     EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext,
 };
@@ -1391,64 +1390,80 @@ declare_lint! {
     ///
     /// ### Explanation
     ///
-    /// The trait bounds in a type alias are currently ignored, and should not
-    /// be included to avoid confusion. This was previously allowed
-    /// unintentionally; this may become a hard error in the future.
+    /// Trait and lifetime bounds on generic parameters and in where clauses of
+    /// type aliases are not checked at usage sites of the type alias. Moreover,
+    /// they are not thoroughly checked for correctness at their definition site
+    /// either similar to the aliased type.
+    ///
+    /// This is a known limitation of the type checker that may be lifted in a
+    /// future edition. Permitting such bounds in light of this was unintentional.
+    ///
+    /// While these bounds may have secondary effects such as enabling the use of
+    /// "shorthand" associated type paths[^1] and affecting the default trait
+    /// object lifetime[^2] of trait object types passed to the type alias, this
+    /// should not have been allowed until the aforementioned restrictions of the
+    /// type checker have been lifted.
+    ///
+    /// Using such bounds is highly discouraged as they are actively misleading.
+    ///
+    /// [^1]: I.e., paths of the form `T::Assoc` where `T` is a type parameter
+    /// bounded by trait `Trait` which defines an associated type called `Assoc`
+    /// as opposed to a fully qualified path of the form `<T as Trait>::Assoc`.
+    /// [^2]: <https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes>
     TYPE_ALIAS_BOUNDS,
     Warn,
     "bounds in type aliases are not enforced"
 }
 
-declare_lint_pass!(
-    /// Lint for trait and lifetime bounds in type aliases being mostly ignored.
-    /// They are relevant when using associated types, but otherwise neither checked
-    /// at definition site nor enforced at use site.
-    TypeAliasBounds => [TYPE_ALIAS_BOUNDS]
-);
+declare_lint_pass!(TypeAliasBounds => [TYPE_ALIAS_BOUNDS]);
 
 impl TypeAliasBounds {
-    pub(crate) fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool {
-        match *qpath {
-            hir::QPath::TypeRelative(ty, _) => {
-                // If this is a type variable, we found a `T::Assoc`.
-                match ty.kind {
-                    hir::TyKind::Path(hir::QPath::Resolved(None, path)) => {
-                        matches!(path.res, Res::Def(DefKind::TyParam, _))
-                    }
-                    _ => false,
-                }
-            }
-            hir::QPath::Resolved(..) | hir::QPath::LangItem(..) => false,
+    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
+            && 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()
+        {
+            return true;
         }
+        false
     }
 }
 
 impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
     fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
-        let hir::ItemKind::TyAlias(hir_ty, type_alias_generics) = &item.kind else { return };
+        let hir::ItemKind::TyAlias(hir_ty, generics) = item.kind else { return };
 
-        // Bounds of lazy type aliases and TAITs are respected.
-        if cx.tcx.type_alias_is_lazy(item.owner_id) {
+        // There must not be a where clause.
+        if generics.predicates.is_empty() {
             return;
         }
 
-        let ty = cx.tcx.type_of(item.owner_id).skip_binder();
-        if ty.has_inherent_projections() {
-            // Bounds of type aliases that contain opaque types or inherent projections are
-            // respected. E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`, `type X =
-            // Type::Inherent;`.
+        // Bounds of lazy type aliases and TAITs are respected.
+        if cx.tcx.type_alias_is_lazy(item.owner_id) {
             return;
         }
 
-        // There must not be a where clause
-        if type_alias_generics.predicates.is_empty() {
+        // FIXME(generic_const_exprs): Revisit this before stabilization.
+        // See also `tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs`.
+        let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
+        if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION)
+            && cx.tcx.features().generic_const_exprs
+        {
             return;
         }
 
+        // NOTE(inherent_associated_types): While we currently do take some bounds in type
+        // aliases into consideration during IAT *selection*, we don't perform full use+def
+        // site wfchecking for such type aliases. Therefore TAB should still trigger.
+        // See also `tests/ui/associated-inherent-types/type-alias-bounds.rs`.
+
         let mut where_spans = Vec::new();
         let mut inline_spans = Vec::new();
         let mut inline_sugg = Vec::new();
-        for p in type_alias_generics.predicates {
+
+        for p in generics.predicates {
             let span = p.span();
             if p.in_where_clause() {
                 where_spans.push(span);
@@ -1460,37 +1475,57 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
             }
         }
 
-        let mut suggested_changing_assoc_types = false;
-        if !where_spans.is_empty() {
-            let sub = (!suggested_changing_assoc_types).then(|| {
-                suggested_changing_assoc_types = true;
-                SuggestChangingAssocTypes { ty: hir_ty }
-            });
+        let mut ty = Some(hir_ty);
+        let enable_feat_help = cx.tcx.sess.is_nightly_build();
+
+        if let [.., label_sp] = *where_spans {
             cx.emit_span_lint(
                 TYPE_ALIAS_BOUNDS,
                 where_spans,
-                BuiltinTypeAliasWhereClause {
-                    suggestion: type_alias_generics.where_clause_span,
-                    sub,
+                BuiltinTypeAliasBounds {
+                    in_where_clause: true,
+                    label: label_sp,
+                    enable_feat_help,
+                    suggestions: vec![(generics.where_clause_span, String::new())],
+                    preds: generics.predicates,
+                    ty: ty.take(),
                 },
             );
         }
-
-        if !inline_spans.is_empty() {
-            let suggestion = BuiltinTypeAliasGenericBoundsSuggestion { suggestions: inline_sugg };
-            let sub = (!suggested_changing_assoc_types).then(|| {
-                suggested_changing_assoc_types = true;
-                SuggestChangingAssocTypes { ty: hir_ty }
-            });
+        if let [.., label_sp] = *inline_spans {
             cx.emit_span_lint(
                 TYPE_ALIAS_BOUNDS,
                 inline_spans,
-                BuiltinTypeAliasGenericBounds { suggestion, sub },
+                BuiltinTypeAliasBounds {
+                    in_where_clause: false,
+                    label: label_sp,
+                    enable_feat_help,
+                    suggestions: inline_sugg,
+                    preds: generics.predicates,
+                    ty,
+                },
             );
         }
     }
 }
 
+pub(crate) struct ShorthandAssocTyCollector {
+    pub(crate) qselves: Vec<Span>,
+}
+
+impl hir::intravisit::Visitor<'_> for ShorthandAssocTyCollector {
+    fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, _: Span) {
+        // Look for "type-parameter shorthand-associated-types". I.e., paths of the
+        // form `T::Assoc` with `T` type param. These are reliant on trait bounds.
+        if let hir::QPath::TypeRelative(qself, _) = qpath
+            && qself.as_generic_param().is_some()
+        {
+            self.qselves.push(qself.span);
+        }
+        hir::intravisit::walk_qpath(self, qpath, id)
+    }
+}
+
 declare_lint! {
     /// The `trivial_bounds` lint detects trait bounds that don't depend on
     /// any type parameters.
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 1f0954c6e9f..b669a3c6288 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -2,14 +2,16 @@
 #![allow(rustc::untranslatable_diagnostic)]
 use std::num::NonZero;
 
-use crate::errors::RequestedLevel;
+use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds};
+use crate::errors::{OverruledAttributeSub, RequestedLevel};
 use crate::fluent_generated as fluent;
+use crate::LateContext;
 use rustc_errors::{
     codes::*, Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString,
     ElidedLifetimeInPathSubdiag, EmissionGuarantee, LintDiagnostic, MultiSpan, SubdiagMessageOp,
     Subdiagnostic, SuggestionStyle,
 };
-use rustc_hir::{def::Namespace, def_id::DefId};
+use rustc_hir::{self as hir, def::Namespace, def_id::DefId};
 use rustc_macros::{LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::{
     inhabitedness::InhabitedPredicate, Clause, PolyExistentialTraitRef, Ty, TyCtxt,
@@ -22,10 +24,6 @@ use rustc_span::{
     Span, Symbol,
 };
 
-use crate::{
-    builtin::InitError, builtin::TypeAliasBounds, errors::OverruledAttributeSub, LateContext,
-};
-
 // array_into_iter.rs
 #[derive(LintDiagnostic)]
 #[diag(lint_shadowed_into_iter)]
@@ -263,62 +261,6 @@ pub struct BuiltinUnreachablePub<'a> {
     pub help: Option<()>,
 }
 
-pub struct SuggestChangingAssocTypes<'a, 'b> {
-    pub ty: &'a rustc_hir::Ty<'b>,
-}
-
-impl<'a, 'b> Subdiagnostic for SuggestChangingAssocTypes<'a, 'b> {
-    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
-        self,
-        diag: &mut Diag<'_, G>,
-        _f: &F,
-    ) {
-        // Access to associates types should use `<T as Bound>::Assoc`, which does not need a
-        // bound. Let's see if this type does that.
-
-        // We use a HIR visitor to walk the type.
-        use rustc_hir::intravisit::{self, Visitor};
-        struct WalkAssocTypes<'a, 'b, G: EmissionGuarantee> {
-            err: &'a mut Diag<'b, G>,
-        }
-        impl<'a, 'b, G: EmissionGuarantee> Visitor<'_> for WalkAssocTypes<'a, 'b, G> {
-            fn visit_qpath(
-                &mut self,
-                qpath: &rustc_hir::QPath<'_>,
-                id: rustc_hir::HirId,
-                span: Span,
-            ) {
-                if TypeAliasBounds::is_type_variable_assoc(qpath) {
-                    self.err.span_help(span, fluent::lint_builtin_type_alias_bounds_help);
-                }
-                intravisit::walk_qpath(self, qpath, id)
-            }
-        }
-
-        // Let's go for a walk!
-        let mut visitor = WalkAssocTypes { err: diag };
-        visitor.visit_ty(self.ty);
-    }
-}
-
-#[derive(LintDiagnostic)]
-#[diag(lint_builtin_type_alias_where_clause)]
-pub struct BuiltinTypeAliasWhereClause<'a, 'b> {
-    #[suggestion(code = "", applicability = "machine-applicable")]
-    pub suggestion: Span,
-    #[subdiagnostic]
-    pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
-}
-
-#[derive(LintDiagnostic)]
-#[diag(lint_builtin_type_alias_generic_bounds)]
-pub struct BuiltinTypeAliasGenericBounds<'a, 'b> {
-    #[subdiagnostic]
-    pub suggestion: BuiltinTypeAliasGenericBoundsSuggestion,
-    #[subdiagnostic]
-    pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
-}
-
 #[derive(LintDiagnostic)]
 #[diag(lint_macro_expr_fragment_specifier_2024_migration)]
 pub struct MacroExprFragment2024 {
@@ -326,21 +268,72 @@ pub struct MacroExprFragment2024 {
     pub suggestion: Span,
 }
 
-pub struct BuiltinTypeAliasGenericBoundsSuggestion {
+pub struct BuiltinTypeAliasBounds<'a, 'hir> {
+    pub in_where_clause: bool,
+    pub label: Span,
+    pub enable_feat_help: bool,
     pub suggestions: Vec<(Span, String)>,
+    pub preds: &'hir [hir::WherePredicate<'hir>],
+    pub ty: Option<&'a hir::Ty<'hir>>,
 }
 
-impl Subdiagnostic for BuiltinTypeAliasGenericBoundsSuggestion {
-    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
-        self,
-        diag: &mut Diag<'_, G>,
-        _f: &F,
-    ) {
-        diag.multipart_suggestion(
-            fluent::lint_suggestion,
-            self.suggestions,
-            Applicability::MachineApplicable,
-        );
+impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_, '_> {
+    fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
+        diag.primary_message(if self.in_where_clause {
+            fluent::lint_builtin_type_alias_bounds_where_clause
+        } else {
+            fluent::lint_builtin_type_alias_bounds_param_bounds
+        });
+        diag.span_label(self.label, fluent::lint_builtin_type_alias_bounds_label);
+        diag.note(fluent::lint_builtin_type_alias_bounds_limitation_note);
+        if self.enable_feat_help {
+            diag.help(fluent::lint_builtin_type_alias_bounds_enable_feat_help);
+        }
+
+        // We perform the walk in here instead of in `<TypeAliasBounds as LateLintPass>` to
+        // avoid doing throwaway work in case the lint ends up getting suppressed.
+        let mut collector = ShorthandAssocTyCollector { qselves: Vec::new() };
+        if let Some(ty) = self.ty {
+            hir::intravisit::Visitor::visit_ty(&mut collector, ty);
+        }
+
+        let affect_object_lifetime_defaults = self
+            .preds
+            .iter()
+            .filter(|pred| pred.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.
+        // The user first needs to fully qualify the assoc tys.
+        let applicability = if !collector.qselves.is_empty() || affect_object_lifetime_defaults {
+            Applicability::MaybeIncorrect
+        } else {
+            Applicability::MachineApplicable
+        };
+
+        diag.arg("count", self.suggestions.len());
+        diag.multipart_suggestion(fluent::lint_suggestion, self.suggestions, applicability);
+
+        // Suggest fully qualifying paths of the form `T::Assoc` with `T` type param via
+        // `<T as /* Trait */>::Assoc` to remove their reliance on any type param bounds.
+        //
+        // Instead of attempting to figure out the necessary trait ref, just use a
+        // placeholder. Since we don't record type-dependent resolutions for non-body
+        // items like type aliases, we can't simply deduce the corresp. trait from
+        // the HIR path alone without rerunning parts of HIR ty lowering here
+        // (namely `probe_single_ty_param_bound_for_assoc_ty`) which is infeasible.
+        //
+        // (We could employ some simple heuristics but that's likely not worth it).
+        for qself in collector.qselves {
+            diag.multipart_suggestion(
+                fluent::lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg,
+                vec![
+                    (qself.shrink_to_lo(), "<".into()),
+                    (qself.shrink_to_hi(), " as /* Trait */>".into()),
+                ],
+                Applicability::HasPlaceholders,
+            );
+        }
     }
 }
 
diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs
index 2f8eea6cd18..93c606a954e 100644
--- a/compiler/rustc_lint/src/non_local_def.rs
+++ b/compiler/rustc_lint/src/non_local_def.rs
@@ -429,7 +429,7 @@ fn ty_has_local_parent(
             path_has_local_parent(ty_path, cx, impl_parent, impl_parent_parent)
         }
         TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => path_has_local_parent(
-            principle_poly_trait_ref.trait_ref.path,
+            principle_poly_trait_ref.0.trait_ref.path,
             cx,
             impl_parent,
             impl_parent_parent,
@@ -527,7 +527,7 @@ fn self_ty_kind_for_diagnostic(ty: &rustc_hir::Ty<'_>, tcx: TyCtxt<'_>) -> (Span
                 .to_string(),
         ),
         TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => {
-            let path = &principle_poly_trait_ref.trait_ref.path;
+            let path = &principle_poly_trait_ref.0.trait_ref.path;
             (
                 path_span_without_args(path),
                 path.res
diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs
index 6983e7abbd6..552245f0cdd 100644
--- a/compiler/rustc_lint/src/traits.rs
+++ b/compiler/rustc_lint/src/traits.rs
@@ -113,9 +113,11 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
 
     fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) {
         let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return };
-        for bound in &bounds[..] {
+        for (bound, modifier) in &bounds[..] {
             let def_id = bound.trait_ref.trait_def_id();
-            if cx.tcx.lang_items().drop_trait() == def_id {
+            if cx.tcx.lang_items().drop_trait() == def_id
+                && *modifier != hir::TraitBoundModifier::Maybe
+            {
                 let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return };
                 cx.emit_span_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id });
             }
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index d2b444a066b..da98e3b9f46 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -1864,9 +1864,9 @@ impl<'tcx> Ty<'tcx> {
             // Definitely absolutely not copy.
             ty::Ref(_, _, hir::Mutability::Mut) => false,
 
-            // Thin pointers & thin shared references are pure-clone-copy, but for
-            // anything with custom metadata it might be more complicated.
-            ty::Ref(_, _, hir::Mutability::Not) | ty::RawPtr(..) => false,
+            // The standard library has a blanket Copy impl for shared references and raw pointers,
+            // for all unsized types.
+            ty::Ref(_, _, hir::Mutability::Not) | ty::RawPtr(..) => true,
 
             ty::Coroutine(..) | ty::CoroutineWitness(..) => false,
 
diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs
index 8209e5e2711..58fdc2d9e45 100644
--- a/compiler/rustc_mir_transform/src/instsimplify.rs
+++ b/compiler/rustc_mir_transform/src/instsimplify.rs
@@ -3,6 +3,7 @@
 use crate::simplify::simplify_duplicate_switch_targets;
 use crate::take_array;
 use rustc_ast::attr;
+use rustc_hir::LangItem;
 use rustc_middle::bug;
 use rustc_middle::mir::*;
 use rustc_middle::ty::layout;
@@ -271,8 +272,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> {
             return;
         }
 
-        let trait_def_id = self.tcx.trait_of_item(fn_def_id);
-        if trait_def_id.is_none() || trait_def_id != self.tcx.lang_items().clone_trait() {
+        if !self.tcx.is_lang_item(fn_def_id, LangItem::CloneFn) {
             return;
         }
 
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index 6835a39cf36..d2f50040821 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -17,7 +17,7 @@ use std::iter;
 
 use crate::{
     abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator,
-    mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify,
+    instsimplify, mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify,
 };
 use rustc_middle::mir::patch::MirPatch;
 use rustc_mir_dataflow::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle};
@@ -154,6 +154,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body<
             &deref_separator::Derefer,
             &remove_noop_landing_pads::RemoveNoopLandingPads,
             &simplify::SimplifyCfg::MakeShim,
+            &instsimplify::InstSimplify,
             &abort_unwinding_calls::AbortUnwindingCalls,
             &add_call_guards::CriticalCallEdges,
         ],
@@ -434,6 +435,9 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -
     match self_ty.kind() {
         ty::FnDef(..) | ty::FnPtr(_) => builder.copy_shim(),
         ty::Closure(_, args) => builder.tuple_like_shim(dest, src, args.as_closure().upvar_tys()),
+        ty::CoroutineClosure(_, args) => {
+            builder.tuple_like_shim(dest, src, args.as_coroutine_closure().upvar_tys())
+        }
         ty::Tuple(..) => builder.tuple_like_shim(dest, src, self_ty.tuple_fields()),
         ty::Coroutine(coroutine_def_id, args) => {
             assert_eq!(tcx.coroutine_movability(*coroutine_def_id), hir::Movability::Movable);
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 770ac9a929e..60beaa0df84 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
@@ -217,7 +217,10 @@ where
         // 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::CoroutineClosure(..) => Err(NoSolution),
+        // 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())])
+        }
 
         // only when `coroutine_clone` is enabled and the coroutine is movable
         // impl Copy/Clone for Coroutine where T: Copy/Clone forall T in (upvars, witnesses)
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 0b2c3044039..535b53a836e 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -457,14 +457,3 @@ impl<'a> Parser<'a> {
         Err(self.dcx().create_err(err))
     }
 }
-
-/// The attributes are complete if all attributes are either a doc comment or a
-/// builtin attribute other than `cfg_attr`.
-pub fn is_complete(attrs: &[ast::Attribute]) -> bool {
-    attrs.iter().all(|attr| {
-        attr.is_doc_comment()
-            || attr.ident().is_some_and(|ident| {
-                ident.name != sym::cfg_attr && rustc_feature::is_builtin_attr_name(ident.name)
-            })
-    })
-}
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index dc5f98f7be8..5dc49ea51d1 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -60,10 +60,6 @@ impl AttrWrapper {
     pub fn is_empty(&self) -> bool {
         self.attrs.is_empty()
     }
-
-    pub fn is_complete(&self) -> bool {
-        crate::parser::attr::is_complete(&self.attrs)
-    }
 }
 
 /// Returns `true` if `attrs` contains a `cfg` or `cfg_attr` attribute
@@ -114,17 +110,15 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
             replace_ranges.sort_by_key(|(range, _)| range.start);
 
             #[cfg(debug_assertions)]
-            {
-                for [(range, tokens), (next_range, next_tokens)] in replace_ranges.array_windows() {
-                    assert!(
-                        range.end <= next_range.start || range.end >= next_range.end,
-                        "Replace ranges should either be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})",
-                        range,
-                        tokens,
-                        next_range,
-                        next_tokens,
-                    );
-                }
+            for [(range, tokens), (next_range, next_tokens)] in replace_ranges.array_windows() {
+                assert!(
+                    range.end <= next_range.start || range.end >= next_range.end,
+                    "Replace ranges should either be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})",
+                    range,
+                    tokens,
+                    next_range,
+                    next_tokens,
+                );
             }
 
             // Process the replace ranges, starting from the highest start
@@ -137,9 +131,9 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
             // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }`
             //
             // By starting processing from the replace range with the greatest
-            // start position, we ensure that any replace range which encloses
-            // another replace range will capture the *replaced* tokens for the inner
-            // range, not the original tokens.
+            // start position, we ensure that any (outer) replace range which
+            // encloses another (inner) replace range will fully overwrite the
+            // inner range's replacement.
             for (range, target) in replace_ranges.into_iter().rev() {
                 assert!(!range.is_empty(), "Cannot replace an empty range: {range:?}");
 
@@ -199,20 +193,20 @@ impl<'a> Parser<'a> {
         force_collect: ForceCollect,
         f: impl FnOnce(&mut Self, ast::AttrVec) -> PResult<'a, (R, bool)>,
     ) -> PResult<'a, R> {
-        // Skip collection when nothing could observe the collected tokens, i.e.
-        // all of the following conditions hold.
-        // - We are not force collecting tokens (because force collection
-        //   requires tokens by definition).
-        if matches!(force_collect, ForceCollect::No)
-            // - None of our outer attributes require tokens.
-            && attrs.is_complete()
-            // - Our target doesn't support custom inner attributes (custom
+        // We must collect if anything could observe the collected tokens, i.e.
+        // if any of the following conditions hold.
+        // - We are force collecting tokens (because force collection requires
+        //   tokens by definition).
+        let needs_collection = matches!(force_collect, ForceCollect::Yes)
+            // - Any of our outer attributes require tokens.
+            || needs_tokens(&attrs.attrs)
+            // - Our target supports custom inner attributes (custom
             //   inner attribute invocation might require token capturing).
-            && !R::SUPPORTS_CUSTOM_INNER_ATTRS
-            // - We are not in `capture_cfg` mode (which requires tokens if
+            || R::SUPPORTS_CUSTOM_INNER_ATTRS
+            // - We are in `capture_cfg` mode (which requires tokens if
             //   the parsed node has `#[cfg]` or `#[cfg_attr]` attributes).
-            && !self.capture_cfg
-        {
+            || self.capture_cfg;
+        if !needs_collection {
             return Ok(f(self, attrs.attrs)?.0);
         }
 
@@ -250,28 +244,28 @@ impl<'a> Parser<'a> {
             return Ok(ret);
         }
 
-        // This is similar to the "skip collection" check at the start of this
-        // function, but now that we've parsed an AST node we have more
+        // This is similar to the `needs_collection` check at the start of this
+        // function, but now that we've parsed an AST node we have complete
         // information available. (If we return early here that means the
         // setup, such as cloning the token cursor, was unnecessary. That's
         // hard to avoid.)
         //
-        // Skip collection when nothing could observe the collected tokens, i.e.
-        // all of the following conditions hold.
-        // - We are not force collecting tokens.
-        if matches!(force_collect, ForceCollect::No)
-            // - None of our outer *or* inner attributes require tokens.
-            //   (`attrs` was just outer attributes, but `ret.attrs()` is outer
-            //   and inner attributes. That makes this check more precise than
-            //   `attrs.is_complete()` at the start of the function, and we can
-            //   skip the subsequent check on `R::SUPPORTS_CUSTOM_INNER_ATTRS`.
-            && crate::parser::attr::is_complete(ret.attrs())
-            // - We are not in `capture_cfg` mode, or we are but there are no
-            //   `#[cfg]` or `#[cfg_attr]` attributes. (During normal
-            //   non-`capture_cfg` parsing, we don't need any special capturing
-            //   for those attributes, because they're builtin.)
-            && (!self.capture_cfg || !has_cfg_or_cfg_attr(ret.attrs()))
-        {
+        // We must collect if anything could observe the collected tokens, i.e.
+        // if any of the following conditions hold.
+        // - We are force collecting tokens.
+        let needs_collection = matches!(force_collect, ForceCollect::Yes)
+            // - Any of our outer *or* inner attributes require tokens.
+            //   (`attr.attrs` was just outer attributes, but `ret.attrs()` is
+            //   outer and inner attributes. So this check is more precise than
+            //   the earlier `needs_tokens` check, and we don't need to
+            //   check `R::SUPPORTS_CUSTOM_INNER_ATTRS`.)
+            || needs_tokens(ret.attrs())
+            // - We are in `capture_cfg` mode and there are `#[cfg]` or
+            //   `#[cfg_attr]` attributes. (During normal non-`capture_cfg`
+            //   parsing, we don't need any special capturing for those
+            //   attributes, because they're builtin.)
+            || (self.capture_cfg && has_cfg_or_cfg_attr(ret.attrs()));
+        if !needs_collection {
             return Ok(ret);
         }
 
@@ -297,11 +291,13 @@ impl<'a> Parser<'a> {
         // with `None`, which means the relevant tokens will be removed. (More
         // details below.)
         let mut inner_attr_replace_ranges = Vec::new();
-        for inner_attr in ret.attrs().iter().filter(|a| a.style == ast::AttrStyle::Inner) {
-            if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&inner_attr.id) {
-                inner_attr_replace_ranges.push((attr_range, None));
-            } else {
-                self.dcx().span_delayed_bug(inner_attr.span, "Missing token range for attribute");
+        for attr in ret.attrs() {
+            if attr.style == ast::AttrStyle::Inner {
+                if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&attr.id) {
+                    inner_attr_replace_ranges.push((attr_range, None));
+                } else {
+                    self.dcx().span_delayed_bug(attr.span, "Missing token range for attribute");
+                }
             }
         }
 
@@ -337,8 +333,7 @@ impl<'a> Parser<'a> {
         // When parsing `m`:
         // - `start_pos..end_pos` is `0..34` (`mod m`, excluding the `#[cfg_eval]` attribute).
         // - `inner_attr_replace_ranges` is empty.
-        // - `replace_range_start..replace_ranges_end` has two entries.
-        //   - One to delete the inner attribute (`17..27`), obtained when parsing `g` (see above).
+        // - `replace_range_start..replace_ranges_end` has one entry.
         //   - One `AttrsTarget` (added below when parsing `g`) to replace all of `g` (`3..33`,
         //     including its outer attribute), with:
         //     - `attrs`: includes the outer and the inner attr.
@@ -369,12 +364,10 @@ impl<'a> Parser<'a> {
 
             // What is the status here when parsing the example code at the top of this method?
             //
-            // When parsing `g`, we add two entries:
+            // When parsing `g`, we add one entry:
             // - The `start_pos..end_pos` (`3..33`) entry has a new `AttrsTarget` with:
             //   - `attrs`: includes the outer and the inner attr.
             //   - `tokens`: lazy tokens for `g` (with its inner attr deleted).
-            // - `inner_attr_replace_ranges` contains the one entry to delete the inner attr's
-            //   tokens (`17..27`).
             //
             // When parsing `m`, we do nothing here.
 
@@ -384,7 +377,6 @@ impl<'a> Parser<'a> {
             let start_pos = if has_outer_attrs { attrs.start_pos } else { start_pos };
             let target = AttrsTarget { attrs: ret.attrs().iter().cloned().collect(), tokens };
             self.capture_state.replace_ranges.push((start_pos..end_pos, Some(target)));
-            self.capture_state.replace_ranges.extend(inner_attr_replace_ranges);
         } else if matches!(self.capture_state.capturing, Capturing::No) {
             // Only clear the ranges once we've finished capturing entirely, i.e. we've finished
             // the outermost call to this method.
@@ -461,6 +453,19 @@ fn make_attr_token_stream(
     AttrTokenStream::new(stack_top.inner)
 }
 
+/// Tokens are needed if:
+/// - any non-single-segment attributes (other than doc comments) are present; or
+/// - any `cfg_attr` attributes are present;
+/// - any single-segment, non-builtin attributes are present.
+fn needs_tokens(attrs: &[ast::Attribute]) -> bool {
+    attrs.iter().any(|attr| match attr.ident() {
+        None => !attr.is_doc_comment(),
+        Some(ident) => {
+            ident.name == sym::cfg_attr || !rustc_feature::is_builtin_attr_name(ident.name)
+        }
+    })
+}
+
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
 #[cfg(target_pointer_width = "64")]
 mod size_asserts {
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 9aaf4b99243..112855e6d1f 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -2483,12 +2483,15 @@ impl<'a> Parser<'a> {
     /// `check_pub` adds additional `pub` to the checks in case users place it
     /// wrongly, can be used to ensure `pub` never comes after `default`.
     pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> bool {
+        const ALL_QUALS: &[Symbol] =
+            &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern];
+
         // We use an over-approximation here.
         // `const const`, `fn const` won't parse, but we're not stepping over other syntax either.
         // `pub` is added in case users got confused with the ordering like `async pub fn`,
         // only if it wasn't preceded by `default` as `default pub` is invalid.
         let quals: &[Symbol] = if check_pub {
-            &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern]
+            ALL_QUALS
         } else {
             &[kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern]
         };
@@ -2518,9 +2521,9 @@ impl<'a> Parser<'a> {
             || self.check_keyword_case(kw::Extern, case)
                 && self.look_ahead(1, |t| t.can_begin_string_literal())
                 && (self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case)) ||
-                    // this branch is only for better diagnostic in later, `pub` is not allowed here
+                    // this branch is only for better diagnostics; `pub`, `unsafe`, etc. are not allowed here
                     (self.may_recover()
-                        && self.look_ahead(2, |t| t.is_keyword(kw::Pub))
+                        && self.look_ahead(2, |t| ALL_QUALS.iter().any(|&kw| t.is_keyword(kw)))
                         && self.look_ahead(3, |t| t.is_keyword_case(kw::Fn, case))))
     }
 
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 55514883cb1..239bc8e7acc 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -54,7 +54,24 @@ impl Publicness {
     }
 }
 
-fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool {
+fn adt_of<'tcx>(ty: &hir::Ty<'tcx>) -> Option<(LocalDefId, DefKind)> {
+    match ty.kind {
+        TyKind::Path(hir::QPath::Resolved(_, path)) => {
+            if let Res::Def(def_kind, def_id) = path.res
+                && let Some(local_def_id) = def_id.as_local()
+            {
+                Some((local_def_id, def_kind))
+            } else {
+                None
+            }
+        }
+        TyKind::Slice(ty) | TyKind::Array(ty, _) => adt_of(ty),
+        TyKind::Ptr(ty) | TyKind::Ref(_, ty) => adt_of(ty.ty),
+        _ => None,
+    }
+}
+
+fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: LocalDefId) -> bool {
     // treat PhantomData and positional ZST as public,
     // we don't want to lint types which only have them,
     // cause it's a common way to use such types to check things like well-formedness
@@ -79,10 +96,7 @@ fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool {
 /// for enum and union, just check they are public,
 /// and doesn't solve types like &T for now, just skip them
 fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness {
-    if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
-        && let Res::Def(def_kind, def_id) = path.res
-        && def_id.is_local()
-    {
+    if let Some((def_id, def_kind)) = adt_of(ty) {
         return match def_kind {
             DefKind::Enum | DefKind::Union => {
                 let ty_is_public = tcx.visibility(def_id).is_public();
@@ -565,10 +579,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
     }
 
     fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: LocalDefId) -> bool {
-        if let TyKind::Path(hir::QPath::Resolved(_, path)) =
-            self.tcx.hir().item(impl_id).expect_impl().self_ty.kind
-            && let Res::Def(def_kind, def_id) = path.res
-            && let Some(local_def_id) = def_id.as_local()
+        if let Some((local_def_id, def_kind)) =
+            adt_of(self.tcx.hir().item(impl_id).expect_impl().self_ty)
             && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
         {
             if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id
@@ -915,7 +927,7 @@ fn create_and_seed_worklist(
                     match tcx.def_kind(id) {
                         DefKind::Impl { .. } => false,
                         DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer),
-                        DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(),
+                        DefKind::Struct => struct_all_fields_are_public(tcx, id) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(),
                         _ => true
                     })
                 .map(|id| (id, ComesFromAllowExpect::No))
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 046ae5fc593..df80f4df5b9 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -1448,7 +1448,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         );
 
         if macro_kind == MacroKind::Bang && ident.name == sym::macro_rules {
-            err.subdiagnostic(MaybeMissingMacroRulesName { span: ident.span });
+            let label_span = ident.span.shrink_to_hi();
+            let mut spans = MultiSpan::from_span(label_span);
+            spans.push_span_label(label_span, "put a macro name here");
+            err.subdiagnostic(MaybeMissingMacroRulesName { spans: spans });
             return;
         }
 
diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs
index 0a68231c6fe..698147765b3 100644
--- a/compiler/rustc_resolve/src/errors.rs
+++ b/compiler/rustc_resolve/src/errors.rs
@@ -669,7 +669,7 @@ pub(crate) struct MacroSuggMovePosition {
 #[note(resolve_missing_macro_rules_name)]
 pub(crate) struct MaybeMissingMacroRulesName {
     #[primary_span]
-    pub(crate) span: Span,
+    pub(crate) spans: MultiSpan,
 }
 
 #[derive(Subdiagnostic)]
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 2b30ca8a894..cfd263d5951 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -557,6 +557,7 @@ symbols! {
         clobber_abi,
         clone,
         clone_closures,
+        clone_fn,
         clone_from,
         closure,
         closure_lifetime_binder,
@@ -1229,6 +1230,7 @@ symbols! {
         modifiers,
         module,
         module_path,
+        more_maybe_bounds,
         more_qualified_paths,
         more_struct_aliases,
         movbe_target_feature,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs
index b91b755d683..a892ce58861 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs
@@ -84,7 +84,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
             }
 
             hir::TyKind::TraitObject(bounds, ..) => {
-                for bound in bounds {
+                for (bound, _) in bounds {
                     self.current_index.shift_in(1);
                     self.visit_poly_trait_ref(bound);
                     self.current_index.shift_out(1);
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
index ce157ff3dc8..e5d97976534 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
@@ -607,7 +607,7 @@ impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> {
             _,
         ) = t.kind
         {
-            for ptr in poly_trait_refs {
+            for (ptr, _) in poly_trait_refs {
                 if Some(self.1) == ptr.trait_ref.trait_def_id() {
                     self.0.push(ptr.span);
                 }
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
index 87fdc5ddff8..89ae6f4ccab 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
@@ -437,7 +437,7 @@ pub fn report_object_safety_error<'tcx>(
         if tcx.parent_hir_node(hir_id).fn_sig().is_some() {
             // Do not suggest `impl Trait` when dealing with things like super-traits.
             err.span_suggestion_verbose(
-                ty.span.until(trait_ref.span),
+                ty.span.until(trait_ref.0.span),
                 "consider using an opaque type instead",
                 "impl ",
                 Applicability::MaybeIncorrect,
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 885216e6216..d1381d24764 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -3040,11 +3040,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     match ty.kind {
                         hir::TyKind::TraitObject(traits, _, _) => {
                             let (span, kw) = match traits {
-                                [first, ..] if first.span.lo() == ty.span.lo() => {
+                                [(first, _), ..] if first.span.lo() == ty.span.lo() => {
                                     // Missing `dyn` in front of trait object.
                                     (ty.span.shrink_to_lo(), "dyn ")
                                 }
-                                [first, ..] => (ty.span.until(first.span), ""),
+                                [(first, _), ..] => (ty.span.until(first.span), ""),
                                 [] => span_bug!(ty.span, "trait object with no traits: {ty:?}"),
                             };
                             let needs_parens = traits.len() != 1;
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index c007cd5314a..699c05466bd 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -2262,8 +2262,21 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 }
             }
 
-            // FIXME(async_closures): These are never clone, for now.
-            ty::CoroutineClosure(_, _) => None,
+            ty::CoroutineClosure(_, args) => {
+                // (*) binder moved here
+                let ty = self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty());
+                if let ty::Infer(ty::TyVar(_)) = ty.kind() {
+                    // Not yet resolved.
+                    Ambiguous
+                } else {
+                    Where(
+                        obligation
+                            .predicate
+                            .rebind(args.as_coroutine_closure().upvar_tys().to_vec()),
+                    )
+                }
+            }
+
             // `Copy` and `Clone` are automatically implemented for an anonymous adt
             // if all of its fields are `Copy` and `Clone`
             ty::Adt(adt, args) if adt.is_anonymous() => {
diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs
index 6ec38b78fc2..d5e114b2175 100644
--- a/compiler/rustc_type_ir/src/visit.rs
+++ b/compiler/rustc_type_ir/src/visit.rs
@@ -241,10 +241,6 @@ pub trait TypeVisitableExt<I: Interner>: TypeVisitable<I> {
         self.has_type_flags(TypeFlags::HAS_ALIAS)
     }
 
-    fn has_inherent_projections(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_TY_INHERENT)
-    }
-
     fn has_opaque_types(&self) -> bool {
         self.has_type_flags(TypeFlags::HAS_TY_OPAQUE)
     }