about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs18
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/body.rs15
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/mod.rs1
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs110
-rw-r--r--compiler/rustc_hir/src/attrs/data_structures.rs3
-rw-r--r--compiler/rustc_hir/src/attrs/encode_cross_crate.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs200
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs34
-rw-r--r--compiler/rustc_mir_build/src/check_tail_calls.rs6
-rw-r--r--compiler/rustc_mir_transform/src/simplify.rs1
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs43
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/mod.rs52
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/trait_goals.rs22
-rw-r--r--compiler/rustc_parse/messages.ftl11
-rw-r--r--compiler/rustc_parse/src/errors.rs28
-rw-r--r--compiler/rustc_parse/src/parser/item.rs3
-rw-r--r--compiler/rustc_parse/src/parser/path.rs31
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs28
-rw-r--r--compiler/rustc_passes/src/check_attr.rs10
20 files changed, 356 insertions, 263 deletions
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 1245d489754..fb42cfea30b 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -98,7 +98,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             }
 
             let expr_hir_id = self.lower_node_id(e.id);
-            self.lower_attrs(expr_hir_id, &e.attrs, e.span);
+            let attrs = self.lower_attrs(expr_hir_id, &e.attrs, e.span);
 
             let kind = match &e.kind {
                 ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)),
@@ -232,10 +232,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         *fn_arg_span,
                     ),
                     None => self.lower_expr_closure(
+                        attrs,
                         binder,
                         *capture_clause,
                         e.id,
-                        expr_hir_id,
                         *constness,
                         *movability,
                         fn_decl,
@@ -1052,10 +1052,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
     fn lower_expr_closure(
         &mut self,
+        attrs: &[rustc_hir::Attribute],
         binder: &ClosureBinder,
         capture_clause: CaptureBy,
         closure_id: NodeId,
-        closure_hir_id: hir::HirId,
         constness: Const,
         movability: Movability,
         decl: &FnDecl,
@@ -1067,15 +1067,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let (binder_clause, generic_params) = self.lower_closure_binder(binder);
 
         let (body_id, closure_kind) = self.with_new_scopes(fn_decl_span, move |this| {
-            let mut coroutine_kind = if this
-                .attrs
-                .get(&closure_hir_id.local_id)
-                .is_some_and(|attrs| attrs.iter().any(|attr| attr.has_name(sym::coroutine)))
-            {
-                Some(hir::CoroutineKind::Coroutine(Movability::Movable))
-            } else {
-                None
-            };
+
+            let mut coroutine_kind = find_attr!(attrs, AttributeKind::Coroutine(_) => hir::CoroutineKind::Coroutine(Movability::Movable));
+
             // FIXME(contracts): Support contracts on closures?
             let body_id = this.lower_fn_body(decl, None, |this| {
                 this.coroutine_kind = coroutine_kind;
diff --git a/compiler/rustc_attr_parsing/src/attributes/body.rs b/compiler/rustc_attr_parsing/src/attributes/body.rs
new file mode 100644
index 00000000000..ab9330216f6
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/body.rs
@@ -0,0 +1,15 @@
+//! Attributes that can be found in function body.
+
+use rustc_hir::attrs::AttributeKind;
+use rustc_span::{Symbol, sym};
+
+use super::{NoArgsAttributeParser, OnDuplicate};
+use crate::context::Stage;
+
+pub(crate) struct CoroutineParser;
+
+impl<S: Stage> NoArgsAttributeParser<S> for CoroutineParser {
+    const PATH: &[Symbol] = &[sym::coroutine];
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
+    const CREATE: fn(rustc_span::Span) -> AttributeKind = |span| AttributeKind::Coroutine(span);
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs
index c574ef78bdf..f7946ade6d2 100644
--- a/compiler/rustc_attr_parsing/src/attributes/mod.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs
@@ -26,6 +26,7 @@ use crate::parser::ArgParser;
 use crate::session_diagnostics::UnusedMultiple;
 
 pub(crate) mod allow_unstable;
+pub(crate) mod body;
 pub(crate) mod cfg;
 pub(crate) mod cfg_old;
 pub(crate) mod codegen_attrs;
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index c6599f20c2d..911d2e13310 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -16,6 +16,7 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
 use crate::attributes::allow_unstable::{
     AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser,
 };
+use crate::attributes::body::CoroutineParser;
 use crate::attributes::codegen_attrs::{
     ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser,
     TargetFeatureParser, TrackCallerParser, UsedParser,
@@ -184,6 +185,7 @@ attribute_parsers!(
         Single<WithoutArgs<ConstContinueParser>>,
         Single<WithoutArgs<ConstStabilityIndirectParser>>,
         Single<WithoutArgs<ConstTraitParser>>,
+        Single<WithoutArgs<CoroutineParser>>,
         Single<WithoutArgs<DenyExplicitImplParser>>,
         Single<WithoutArgs<DoNotImplementViaObjectParser>>,
         Single<WithoutArgs<ExportStableParser>>,
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index f5fedbf95c1..148d0de3bab 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -769,9 +769,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
             }
             TerminatorKind::Call { func, args, .. }
             | TerminatorKind::TailCall { func, args, .. } => {
-                let call_source = match term.kind {
-                    TerminatorKind::Call { call_source, .. } => call_source,
-                    TerminatorKind::TailCall { .. } => CallSource::Normal,
+                let (call_source, destination, is_diverging) = match term.kind {
+                    TerminatorKind::Call { call_source, destination, target, .. } => {
+                        (call_source, destination, target.is_none())
+                    }
+                    TerminatorKind::TailCall { .. } => {
+                        (CallSource::Normal, RETURN_PLACE.into(), false)
+                    }
                     _ => unreachable!(),
                 };
 
@@ -845,9 +849,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     );
                 }
 
-                if let TerminatorKind::Call { destination, target, .. } = term.kind {
-                    self.check_call_dest(term, &sig, destination, target, term_location);
-                }
+                self.check_call_dest(term, &sig, destination, is_diverging, term_location);
 
                 // The ordinary liveness rules will ensure that all
                 // regions in the type of the callee are live here. We
@@ -1874,65 +1876,61 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         term: &Terminator<'tcx>,
         sig: &ty::FnSig<'tcx>,
         destination: Place<'tcx>,
-        target: Option<BasicBlock>,
+        is_diverging: bool,
         term_location: Location,
     ) {
         let tcx = self.tcx();
-        match target {
-            Some(_) => {
-                let dest_ty = destination.ty(self.body, tcx).ty;
-                let dest_ty = self.normalize(dest_ty, term_location);
-                let category = match destination.as_local() {
-                    Some(RETURN_PLACE) => {
-                        if let DefiningTy::Const(def_id, _) | DefiningTy::InlineConst(def_id, _) =
-                            self.universal_regions.defining_ty
-                        {
-                            if tcx.is_static(def_id) {
-                                ConstraintCategory::UseAsStatic
-                            } else {
-                                ConstraintCategory::UseAsConst
-                            }
+        if is_diverging {
+            // The signature in this call can reference region variables,
+            // so erase them before calling a query.
+            let output_ty = self.tcx().erase_regions(sig.output());
+            if !output_ty
+                .is_privately_uninhabited(self.tcx(), self.infcx.typing_env(self.infcx.param_env))
+            {
+                span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
+            }
+        } else {
+            let dest_ty = destination.ty(self.body, tcx).ty;
+            let dest_ty = self.normalize(dest_ty, term_location);
+            let category = match destination.as_local() {
+                Some(RETURN_PLACE) => {
+                    if let DefiningTy::Const(def_id, _) | DefiningTy::InlineConst(def_id, _) =
+                        self.universal_regions.defining_ty
+                    {
+                        if tcx.is_static(def_id) {
+                            ConstraintCategory::UseAsStatic
                         } else {
-                            ConstraintCategory::Return(ReturnConstraint::Normal)
+                            ConstraintCategory::UseAsConst
                         }
+                    } else {
+                        ConstraintCategory::Return(ReturnConstraint::Normal)
                     }
-                    Some(l) if !self.body.local_decls[l].is_user_variable() => {
-                        ConstraintCategory::Boring
-                    }
-                    // The return type of a call is interesting for diagnostics.
-                    _ => ConstraintCategory::Assignment,
-                };
-
-                let locations = term_location.to_locations();
-
-                if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations, category) {
-                    span_mirbug!(
-                        self,
-                        term,
-                        "call dest mismatch ({:?} <- {:?}): {:?}",
-                        dest_ty,
-                        sig.output(),
-                        terr
-                    );
                 }
-
-                // When `unsized_fn_params` is not enabled,
-                // this check is done at `check_local`.
-                if self.unsized_feature_enabled() {
-                    let span = term.source_info.span;
-                    self.ensure_place_sized(dest_ty, span);
+                Some(l) if !self.body.local_decls[l].is_user_variable() => {
+                    ConstraintCategory::Boring
                 }
+                // The return type of a call is interesting for diagnostics.
+                _ => ConstraintCategory::Assignment,
+            };
+
+            let locations = term_location.to_locations();
+
+            if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations, category) {
+                span_mirbug!(
+                    self,
+                    term,
+                    "call dest mismatch ({:?} <- {:?}): {:?}",
+                    dest_ty,
+                    sig.output(),
+                    terr
+                );
             }
-            None => {
-                // The signature in this call can reference region variables,
-                // so erase them before calling a query.
-                let output_ty = self.tcx().erase_regions(sig.output());
-                if !output_ty.is_privately_uninhabited(
-                    self.tcx(),
-                    self.infcx.typing_env(self.infcx.param_env),
-                ) {
-                    span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
-                }
+
+            // When `unsized_fn_params` is not enabled,
+            // this check is done at `check_local`.
+            if self.unsized_feature_enabled() {
+                let span = term.source_info.span;
+                self.ensure_place_sized(dest_ty, span);
             }
         }
     }
diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs
index 80618422b56..d9d2ec48948 100644
--- a/compiler/rustc_hir/src/attrs/data_structures.rs
+++ b/compiler/rustc_hir/src/attrs/data_structures.rs
@@ -297,6 +297,9 @@ pub enum AttributeKind {
     /// Represents `#[const_trait]`.
     ConstTrait(Span),
 
+    /// Represents `#[coroutine]`.
+    Coroutine(Span),
+
     /// Represents `#[coverage(..)]`.
     Coverage(Span, CoverageAttrKind),
 
diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
index 9644a597a31..b66e5bbeabe 100644
--- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
+++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
@@ -28,6 +28,7 @@ impl AttributeKind {
             ConstStability { .. } => Yes,
             ConstStabilityIndirect => No,
             ConstTrait(..) => No,
+            Coroutine(..) => No,
             Coverage(..) => No,
             DenyExplicitImpl(..) => No,
             Deprecation { .. } => Yes,
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index e2462c2d465..ce0e51f106f 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -223,60 +223,27 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
             "synthetic HIR should have its `generics_of` explicitly fed"
         ),
 
-        _ => span_bug!(tcx.def_span(def_id), "unhandled node {node:?}"),
+        _ => span_bug!(tcx.def_span(def_id), "generics_of: unexpected node kind {node:?}"),
     };
 
-    enum Defaults {
-        Allowed,
-        // See #36887
-        FutureCompatDisallowed,
-        Deny,
-    }
-
-    let hir_generics = node.generics().unwrap_or(hir::Generics::empty());
-    let (opt_self, allow_defaults) = match node {
-        Node::Item(item) => {
-            match item.kind {
-                ItemKind::Trait(..) | ItemKind::TraitAlias(..) => {
-                    // Add in the self type parameter.
-                    //
-                    // Something of a hack: use the node id for the trait, also as
-                    // the node id for the Self type parameter.
-                    let opt_self = Some(ty::GenericParamDef {
-                        index: 0,
-                        name: kw::SelfUpper,
-                        def_id: def_id.to_def_id(),
-                        pure_wrt_drop: false,
-                        kind: ty::GenericParamDefKind::Type {
-                            has_default: false,
-                            synthetic: false,
-                        },
-                    });
-
-                    (opt_self, Defaults::Allowed)
-                }
-                ItemKind::TyAlias(..)
-                | ItemKind::Enum(..)
-                | ItemKind::Struct(..)
-                | ItemKind::Union(..) => (None, Defaults::Allowed),
-                ItemKind::Const(..) => (None, Defaults::Deny),
-                _ => (None, Defaults::FutureCompatDisallowed),
-            }
-        }
-
-        Node::OpaqueTy(..) => (None, Defaults::Allowed),
-
-        // GATs
-        Node::TraitItem(item) if matches!(item.kind, TraitItemKind::Type(..)) => {
-            (None, Defaults::Deny)
-        }
-        Node::ImplItem(item) if matches!(item.kind, ImplItemKind::Type(..)) => {
-            (None, Defaults::Deny)
-        }
-
-        _ => (None, Defaults::FutureCompatDisallowed),
+    // Add in the self type parameter.
+    let opt_self = if let Node::Item(item) = node
+        && let ItemKind::Trait(..) | ItemKind::TraitAlias(..) = item.kind
+    {
+        // Something of a hack: We reuse the node ID of the trait for the self type parameter.
+        Some(ty::GenericParamDef {
+            index: 0,
+            name: kw::SelfUpper,
+            def_id: def_id.to_def_id(),
+            pure_wrt_drop: false,
+            kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false },
+        })
+    } else {
+        None
     };
 
+    let param_default_policy = param_default_policy(node);
+    let hir_generics = node.generics().unwrap_or(hir::Generics::empty());
     let has_self = opt_self.is_some();
     let mut parent_has_self = false;
     let mut own_start = has_self as u32;
@@ -312,60 +279,53 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
         prev + type_start
     };
 
-    const TYPE_DEFAULT_NOT_ALLOWED: &'static str = "defaults for type parameters are only allowed in \
-    `struct`, `enum`, `type`, or `trait` definitions";
-
-    own_params.extend(hir_generics.params.iter().filter_map(|param| match param.kind {
-        GenericParamKind::Lifetime { .. } => None,
-        GenericParamKind::Type { default, synthetic, .. } => {
-            if default.is_some() {
-                match allow_defaults {
-                    Defaults::Allowed => {}
-                    Defaults::FutureCompatDisallowed => {
-                        tcx.node_span_lint(
-                            lint::builtin::INVALID_TYPE_PARAM_DEFAULT,
-                            param.hir_id,
-                            param.span,
-                            |lint| {
-                                lint.primary_message(TYPE_DEFAULT_NOT_ALLOWED);
-                            },
-                        );
-                    }
-                    Defaults::Deny => {
-                        tcx.dcx().span_err(param.span, TYPE_DEFAULT_NOT_ALLOWED);
+    own_params.extend(hir_generics.params.iter().filter_map(|param| {
+        const MESSAGE: &str = "defaults for generic parameters are not allowed here";
+        let kind = match param.kind {
+            GenericParamKind::Lifetime { .. } => return None,
+            GenericParamKind::Type { default, synthetic } => {
+                if default.is_some() {
+                    match param_default_policy.expect("no policy for generic param default") {
+                        ParamDefaultPolicy::Allowed => {}
+                        ParamDefaultPolicy::FutureCompatForbidden => {
+                            tcx.node_span_lint(
+                                lint::builtin::INVALID_TYPE_PARAM_DEFAULT,
+                                param.hir_id,
+                                param.span,
+                                |lint| {
+                                    lint.primary_message(MESSAGE);
+                                },
+                            );
+                        }
+                        ParamDefaultPolicy::Forbidden => {
+                            tcx.dcx().span_err(param.span, MESSAGE);
+                        }
                     }
                 }
-            }
-
-            let kind = ty::GenericParamDefKind::Type { has_default: default.is_some(), synthetic };
 
-            Some(ty::GenericParamDef {
-                index: next_index(),
-                name: param.name.ident().name,
-                def_id: param.def_id.to_def_id(),
-                pure_wrt_drop: param.pure_wrt_drop,
-                kind,
-            })
-        }
-        GenericParamKind::Const { ty: _, default, synthetic } => {
-            if !matches!(allow_defaults, Defaults::Allowed) && default.is_some() {
-                tcx.dcx().span_err(
-                    param.span,
-                    "defaults for const parameters are only allowed in \
-                    `struct`, `enum`, `type`, or `trait` definitions",
-                );
+                ty::GenericParamDefKind::Type { has_default: default.is_some(), synthetic }
             }
+            GenericParamKind::Const { ty: _, default, synthetic } => {
+                if default.is_some() {
+                    match param_default_policy.expect("no policy for generic param default") {
+                        ParamDefaultPolicy::Allowed => {}
+                        ParamDefaultPolicy::FutureCompatForbidden
+                        | ParamDefaultPolicy::Forbidden => {
+                            tcx.dcx().span_err(param.span, MESSAGE);
+                        }
+                    }
+                }
 
-            let index = next_index();
-
-            Some(ty::GenericParamDef {
-                index,
-                name: param.name.ident().name,
-                def_id: param.def_id.to_def_id(),
-                pure_wrt_drop: param.pure_wrt_drop,
-                kind: ty::GenericParamDefKind::Const { has_default: default.is_some(), synthetic },
-            })
-        }
+                ty::GenericParamDefKind::Const { has_default: default.is_some(), synthetic }
+            }
+        };
+        Some(ty::GenericParamDef {
+            index: next_index(),
+            name: param.name.ident().name,
+            def_id: param.def_id.to_def_id(),
+            pure_wrt_drop: param.pure_wrt_drop,
+            kind,
+        })
     }));
 
     // provide junk type parameter defs - the only place that
@@ -438,6 +398,48 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
     }
 }
 
+#[derive(Clone, Copy)]
+enum ParamDefaultPolicy {
+    Allowed,
+    /// Tracked in <https://github.com/rust-lang/rust/issues/36887>.
+    FutureCompatForbidden,
+    Forbidden,
+}
+
+fn param_default_policy(node: Node<'_>) -> Option<ParamDefaultPolicy> {
+    use rustc_hir::*;
+
+    Some(match node {
+        Node::Item(item) => match item.kind {
+            ItemKind::Trait(..)
+            | ItemKind::TraitAlias(..)
+            | ItemKind::TyAlias(..)
+            | ItemKind::Enum(..)
+            | ItemKind::Struct(..)
+            | ItemKind::Union(..) => ParamDefaultPolicy::Allowed,
+            ItemKind::Fn { .. } | ItemKind::Impl(_) => ParamDefaultPolicy::FutureCompatForbidden,
+            // Re. GCI, we're not bound by backward compatibility.
+            ItemKind::Const(..) => ParamDefaultPolicy::Forbidden,
+            _ => return None,
+        },
+        Node::TraitItem(item) => match item.kind {
+            // Re. GATs and GACs (generic_const_items), we're not bound by backward compatibility.
+            TraitItemKind::Const(..) | TraitItemKind::Type(..) => ParamDefaultPolicy::Forbidden,
+            TraitItemKind::Fn(..) => ParamDefaultPolicy::FutureCompatForbidden,
+        },
+        Node::ImplItem(item) => match item.kind {
+            // Re. GATs and GACs (generic_const_items), we're not bound by backward compatibility.
+            ImplItemKind::Const(..) | ImplItemKind::Type(..) => ParamDefaultPolicy::Forbidden,
+            ImplItemKind::Fn(..) => ParamDefaultPolicy::FutureCompatForbidden,
+        },
+        // Generic params are (semantically) invalid on foreign items. Still, for maximum forward
+        // compatibility, let's hard-reject defaults on them.
+        Node::ForeignItem(_) => ParamDefaultPolicy::Forbidden,
+        Node::OpaqueTy(..) => ParamDefaultPolicy::Allowed,
+        _ => return None,
+    })
+}
+
 fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<Span> {
     struct LateBoundRegionsDetector<'tcx> {
         tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 929ebe1aee1..42a68b29ec7 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -1205,18 +1205,19 @@ macro_rules! visit_place_fns {
             self.super_projection_elem(place_ref, elem, context, location);
         }
 
-        fn super_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
-            let mut context = context;
-
-            if !place.projection.is_empty() {
-                if context.is_use() {
-                    // ^ Only change the context if it is a real use, not a "use" in debuginfo.
-                    context = if context.is_mutating_use() {
-                        PlaceContext::MutatingUse(MutatingUseContext::Projection)
-                    } else {
-                        PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
-                    };
-                }
+        fn super_place(
+            &mut self,
+            place: &Place<'tcx>,
+            mut context: PlaceContext,
+            location: Location,
+        ) {
+            if !place.projection.is_empty() && context.is_use() {
+                // ^ Only change the context if it is a real use, not a "use" in debuginfo.
+                context = if context.is_mutating_use() {
+                    PlaceContext::MutatingUse(MutatingUseContext::Projection)
+                } else {
+                    PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
+                };
             }
 
             self.visit_local(place.local, context, location);
@@ -1239,7 +1240,7 @@ macro_rules! visit_place_fns {
             &mut self,
             _place_ref: PlaceRef<'tcx>,
             elem: PlaceElem<'tcx>,
-            _context: PlaceContext,
+            context: PlaceContext,
             location: Location,
         ) {
             match elem {
@@ -1252,7 +1253,12 @@ macro_rules! visit_place_fns {
                 ProjectionElem::Index(local) => {
                     self.visit_local(
                         local,
-                        PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
+                        if context.is_use() {
+                            // ^ Only change the context if it is a real use, not a "use" in debuginfo.
+                            PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy)
+                        } else {
+                            context
+                        },
                         location,
                     );
                 }
diff --git a/compiler/rustc_mir_build/src/check_tail_calls.rs b/compiler/rustc_mir_build/src/check_tail_calls.rs
index b4c8b20e50f..3ecccb422c4 100644
--- a/compiler/rustc_mir_build/src/check_tail_calls.rs
+++ b/compiler/rustc_mir_build/src/check_tail_calls.rs
@@ -60,9 +60,13 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
         let BodyTy::Fn(caller_sig) = self.thir.body_type else {
             span_bug!(
                 call.span,
-                "`become` outside of functions should have been disallowed by hit_typeck"
+                "`become` outside of functions should have been disallowed by hir_typeck"
             )
         };
+        // While the `caller_sig` does have its regions erased, it does not have its
+        // binders anonymized. We call `erase_regions` once again to anonymize any binders
+        // within the signature, such as in function pointer or `dyn Trait` args.
+        let caller_sig = self.tcx.erase_regions(caller_sig);
 
         let ExprKind::Scope { value, .. } = call.kind else {
             span_bug!(call.span, "expected scope, found: {call:?}")
diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs
index db933da6413..468ef742dfb 100644
--- a/compiler/rustc_mir_transform/src/simplify.rs
+++ b/compiler/rustc_mir_transform/src/simplify.rs
@@ -225,6 +225,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
             current = target;
         }
         let last = current;
+        *changed |= *start != last;
         *start = last;
         while let Some((current, mut terminator)) = terminators.pop() {
             let Terminator { kind: TerminatorKind::Goto { ref mut target }, .. } = terminator
diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
index 231fb3e2964..5e08c3a03d8 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
@@ -959,36 +959,23 @@ where
                 // Even when a trait bound has been proven using a where-bound, we
                 // still need to consider alias-bounds for normalization, see
                 // `tests/ui/next-solver/alias-bound-shadowed-by-env.rs`.
-                let candidates_from_env_and_bounds: Vec<_> = self
+                let mut candidates: Vec<_> = self
                     .assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::EnvAndBounds);
 
                 // We still need to prefer where-bounds over alias-bounds however.
                 // See `tests/ui/winnowing/norm-where-bound-gt-alias-bound.rs`.
-                let mut considered_candidates: Vec<_> = if candidates_from_env_and_bounds
-                    .iter()
-                    .any(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
-                {
-                    candidates_from_env_and_bounds
-                        .into_iter()
-                        .filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
-                        .map(|c| c.result)
-                        .collect()
-                } else {
-                    candidates_from_env_and_bounds.into_iter().map(|c| c.result).collect()
-                };
-
-                // If the trait goal has been proven by using the environment, we want to treat
-                // aliases as rigid if there are no applicable projection bounds in the environment.
-                if considered_candidates.is_empty() {
-                    if let Ok(response) = inject_normalize_to_rigid_candidate(self) {
-                        considered_candidates.push(response);
-                    }
+                if candidates.iter().any(|c| matches!(c.source, CandidateSource::ParamEnv(_))) {
+                    candidates.retain(|c| matches!(c.source, CandidateSource::ParamEnv(_)));
+                } else if candidates.is_empty() {
+                    // If the trait goal has been proven by using the environment, we want to treat
+                    // aliases as rigid if there are no applicable projection bounds in the environment.
+                    return inject_normalize_to_rigid_candidate(self);
                 }
 
-                if let Some(response) = self.try_merge_responses(&considered_candidates) {
+                if let Some(response) = self.try_merge_candidates(&candidates) {
                     Ok(response)
                 } else {
-                    self.flounder(&considered_candidates)
+                    self.flounder(&candidates)
                 }
             }
             TraitGoalProvenVia::Misc => {
@@ -998,11 +985,9 @@ where
                 // Prefer "orphaned" param-env normalization predicates, which are used
                 // (for example, and ideally only) when proving item bounds for an impl.
                 let candidates_from_env: Vec<_> = candidates
-                    .iter()
-                    .filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
-                    .map(|c| c.result)
+                    .extract_if(.., |c| matches!(c.source, CandidateSource::ParamEnv(_)))
                     .collect();
-                if let Some(response) = self.try_merge_responses(&candidates_from_env) {
+                if let Some(response) = self.try_merge_candidates(&candidates_from_env) {
                     return Ok(response);
                 }
 
@@ -1012,12 +997,10 @@ where
                 // means we can just ignore inference constraints and don't have to special-case
                 // constraining the normalized-to `term`.
                 self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates);
-
-                let responses: Vec<_> = candidates.iter().map(|c| c.result).collect();
-                if let Some(response) = self.try_merge_responses(&responses) {
+                if let Some(response) = self.try_merge_candidates(&candidates) {
                     Ok(response)
                 } else {
-                    self.flounder(&responses)
+                    self.flounder(&candidates)
                 }
             }
         }
diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs
index aec9594b834..2feebe270a6 100644
--- a/compiler/rustc_next_trait_solver/src/solve/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs
@@ -29,6 +29,7 @@ use tracing::instrument;
 
 pub use self::eval_ctxt::{EvalCtxt, GenerateProofTree, SolverDelegateEvalExt};
 use crate::delegate::SolverDelegate;
+use crate::solve::assembly::Candidate;
 
 /// How many fixpoint iterations we should attempt inside of the solver before bailing
 /// with overflow.
@@ -244,50 +245,51 @@ where
     ///
     /// In this case we tend to flounder and return ambiguity by calling `[EvalCtxt::flounder]`.
     #[instrument(level = "trace", skip(self), ret)]
-    fn try_merge_responses(
+    fn try_merge_candidates(
         &mut self,
-        responses: &[CanonicalResponse<I>],
+        candidates: &[Candidate<I>],
     ) -> Option<CanonicalResponse<I>> {
-        if responses.is_empty() {
+        if candidates.is_empty() {
             return None;
         }
 
-        let one = responses[0];
-        if responses[1..].iter().all(|&resp| resp == one) {
+        let one: CanonicalResponse<I> = candidates[0].result;
+        if candidates[1..].iter().all(|candidate| candidate.result == one) {
             return Some(one);
         }
 
-        responses
+        candidates
             .iter()
-            .find(|response| {
-                response.value.certainty == Certainty::Yes
-                    && has_no_inference_or_external_constraints(**response)
+            .find(|candidate| {
+                candidate.result.value.certainty == Certainty::Yes
+                    && has_no_inference_or_external_constraints(candidate.result)
             })
-            .copied()
+            .map(|candidate| candidate.result)
     }
 
-    fn bail_with_ambiguity(&mut self, responses: &[CanonicalResponse<I>]) -> CanonicalResponse<I> {
-        debug_assert!(responses.len() > 1);
-        let maybe_cause = responses.iter().fold(MaybeCause::Ambiguity, |maybe_cause, response| {
-            // Pull down the certainty of `Certainty::Yes` to ambiguity when combining
-            // these responses, b/c we're combining more than one response and this we
-            // don't know which one applies.
-            let candidate = match response.value.certainty {
-                Certainty::Yes => MaybeCause::Ambiguity,
-                Certainty::Maybe(candidate) => candidate,
-            };
-            maybe_cause.or(candidate)
-        });
+    fn bail_with_ambiguity(&mut self, candidates: &[Candidate<I>]) -> CanonicalResponse<I> {
+        debug_assert!(candidates.len() > 1);
+        let maybe_cause =
+            candidates.iter().fold(MaybeCause::Ambiguity, |maybe_cause, candidates| {
+                // Pull down the certainty of `Certainty::Yes` to ambiguity when combining
+                // these responses, b/c we're combining more than one response and this we
+                // don't know which one applies.
+                let candidate = match candidates.result.value.certainty {
+                    Certainty::Yes => MaybeCause::Ambiguity,
+                    Certainty::Maybe(candidate) => candidate,
+                };
+                maybe_cause.or(candidate)
+            });
         self.make_ambiguous_response_no_constraints(maybe_cause)
     }
 
     /// If we fail to merge responses we flounder and return overflow or ambiguity.
     #[instrument(level = "trace", skip(self), ret)]
-    fn flounder(&mut self, responses: &[CanonicalResponse<I>]) -> QueryResult<I> {
-        if responses.is_empty() {
+    fn flounder(&mut self, candidates: &[Candidate<I>]) -> QueryResult<I> {
+        if candidates.is_empty() {
             return Err(NoSolution);
         } else {
-            Ok(self.bail_with_ambiguity(responses))
+            Ok(self.bail_with_ambiguity(candidates))
         }
     }
 
diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
index f760c2c07ff..60bae738e61 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -1346,11 +1346,10 @@ where
         mut candidates: Vec<Candidate<I>>,
     ) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
         if let TypingMode::Coherence = self.typing_mode() {
-            let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect();
-            return if let Some(response) = self.try_merge_responses(&all_candidates) {
+            return if let Some(response) = self.try_merge_candidates(&candidates) {
                 Ok((response, Some(TraitGoalProvenVia::Misc)))
             } else {
-                self.flounder(&all_candidates).map(|r| (r, None))
+                self.flounder(&candidates).map(|r| (r, None))
             };
         }
 
@@ -1375,11 +1374,9 @@ where
             .any(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::NonGlobal)));
         if has_non_global_where_bounds {
             let where_bounds: Vec<_> = candidates
-                .iter()
-                .filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
-                .map(|c| c.result)
+                .extract_if(.., |c| matches!(c.source, CandidateSource::ParamEnv(_)))
                 .collect();
-            return if let Some(response) = self.try_merge_responses(&where_bounds) {
+            return if let Some(response) = self.try_merge_candidates(&where_bounds) {
                 Ok((response, Some(TraitGoalProvenVia::ParamEnv)))
             } else {
                 Ok((self.bail_with_ambiguity(&where_bounds), None))
@@ -1388,11 +1385,9 @@ where
 
         if candidates.iter().any(|c| matches!(c.source, CandidateSource::AliasBound)) {
             let alias_bounds: Vec<_> = candidates
-                .iter()
-                .filter(|c| matches!(c.source, CandidateSource::AliasBound))
-                .map(|c| c.result)
+                .extract_if(.., |c| matches!(c.source, CandidateSource::AliasBound))
                 .collect();
-            return if let Some(response) = self.try_merge_responses(&alias_bounds) {
+            return if let Some(response) = self.try_merge_candidates(&alias_bounds) {
                 Ok((response, Some(TraitGoalProvenVia::AliasBound)))
             } else {
                 Ok((self.bail_with_ambiguity(&alias_bounds), None))
@@ -1417,11 +1412,10 @@ where
             TraitGoalProvenVia::Misc
         };
 
-        let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect();
-        if let Some(response) = self.try_merge_responses(&all_candidates) {
+        if let Some(response) = self.try_merge_candidates(&candidates) {
             Ok((response, Some(proven_via)))
         } else {
-            self.flounder(&all_candidates).map(|r| (r, None))
+            self.flounder(&candidates).map(|r| (r, None))
         }
     }
 
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 859118a4ade..7059ffbf375 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -71,6 +71,17 @@ parse_attr_without_generics = attribute without generic parameters
 parse_attribute_on_param_type = attributes cannot be applied to a function parameter's type
     .label = attributes are not allowed here
 
+parse_attribute_on_type = attributes cannot be applied to types
+    .label = attributes are not allowed here
+    .suggestion = remove attribute from here
+
+parse_attribute_on_generic_arg = attributes cannot be applied to generic arguments
+    .label = attributes are not allowed here
+    .suggestion = remove attribute from here
+
+parse_attribute_on_empty_type = attributes cannot be applied here
+    .label = attributes are not allowed here
+
 parse_bad_assoc_type_bounds = bounds on associated types do not belong here
     .label = belongs in `where` clause
 
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 4aaaba01fae..48ff0394d46 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -1490,6 +1490,34 @@ pub(crate) struct AttributeOnParamType {
 }
 
 #[derive(Diagnostic)]
+#[diag(parse_attribute_on_type)]
+pub(crate) struct AttributeOnType {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
+    pub fix_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_attribute_on_generic_arg)]
+pub(crate) struct AttributeOnGenericArg {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
+    pub fix_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_attribute_on_empty_type)]
+pub(crate) struct AttributeOnEmptyType {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(parse_pattern_method_param_without_body, code = E0642)]
 pub(crate) struct PatternMethodParamWithoutBody {
     #[primary_span]
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 65d84b3e3d9..cb7c5649433 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -885,6 +885,9 @@ impl<'a> Parser<'a> {
     /// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`.
     fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemKind> {
         let constness = self.parse_constness(Case::Sensitive);
+        if let Const::Yes(span) = constness {
+            self.psess.gated_spans.gate(sym::const_trait_impl, span);
+        }
         let safety = self.parse_safety(Case::Sensitive);
         // Parse optional `auto` prefix.
         let is_auto = if self.eat_keyword(exp!(Auto)) {
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 1f4049f197f..8e65ab99c5e 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -17,11 +17,11 @@ use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
 use super::{Parser, Restrictions, TokenType};
 use crate::ast::{PatKind, TyKind};
 use crate::errors::{
-    self, FnPathFoundNamedParams, PathFoundAttributeInParams, PathFoundCVariadicParams,
-    PathSingleColon, PathTripleColon,
+    self, AttributeOnEmptyType, AttributeOnGenericArg, FnPathFoundNamedParams,
+    PathFoundAttributeInParams, PathFoundCVariadicParams, PathSingleColon, PathTripleColon,
 };
 use crate::exp;
-use crate::parser::{CommaRecoveryMode, RecoverColon, RecoverComma};
+use crate::parser::{CommaRecoveryMode, ExprKind, RecoverColon, RecoverComma};
 
 /// Specifies how to parse a path.
 #[derive(Copy, Clone, PartialEq)]
@@ -880,6 +880,12 @@ impl<'a> Parser<'a> {
         &mut self,
         ty_generics: Option<&Generics>,
     ) -> PResult<'a, Option<GenericArg>> {
+        let mut attr_span: Option<Span> = None;
+        if self.token == token::Pound && self.look_ahead(1, |t| *t == token::OpenBracket) {
+            let attrs_wrapper = self.parse_outer_attributes()?;
+            let raw_attrs = attrs_wrapper.take_for_recovery(self.psess);
+            attr_span = Some(raw_attrs[0].span.to(raw_attrs.last().unwrap().span));
+        }
         let start = self.token.span;
         let arg = if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
             // Parse lifetime argument.
@@ -934,6 +940,9 @@ impl<'a> Parser<'a> {
             }
         } else if self.token.is_keyword(kw::Const) {
             return self.recover_const_param_declaration(ty_generics);
+        } else if let Some(attr_span) = attr_span {
+            let diag = self.dcx().create_err(AttributeOnEmptyType { span: attr_span });
+            return Err(diag);
         } else {
             // Fall back by trying to parse a const-expr expression. If we successfully do so,
             // then we should report an error that it needs to be wrapped in braces.
@@ -953,6 +962,22 @@ impl<'a> Parser<'a> {
                 }
             }
         };
+
+        if let Some(attr_span) = attr_span {
+            let guar = self.dcx().emit_err(AttributeOnGenericArg {
+                span: attr_span,
+                fix_span: attr_span.until(arg.span()),
+            });
+            return Ok(Some(match arg {
+                GenericArg::Type(_) => GenericArg::Type(self.mk_ty(attr_span, TyKind::Err(guar))),
+                GenericArg::Const(_) => {
+                    let error_expr = self.mk_expr(attr_span, ExprKind::Err(guar));
+                    GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value: error_expr })
+                }
+                GenericArg::Lifetime(lt) => GenericArg::Lifetime(lt),
+            }));
+        }
+
         Ok(Some(arg))
     }
 
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 740dd10ea8b..59048e42e6f 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -14,10 +14,10 @@ use thin_vec::{ThinVec, thin_vec};
 
 use super::{Parser, PathStyle, SeqSep, TokenType, Trailing};
 use crate::errors::{
-    self, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType,
-    FnPtrWithGenerics, FnPtrWithGenericsSugg, HelpUseLatestEdition, InvalidDynKeyword,
-    LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, NestedCVariadicType,
-    ReturnTypesUseThinArrow,
+    self, AttributeOnEmptyType, AttributeOnType, DynAfterMut, ExpectedFnPathFoundFnKeyword,
+    ExpectedMutOrConstInRawPointerType, FnPtrWithGenerics, FnPtrWithGenericsSugg,
+    HelpUseLatestEdition, InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime,
+    NestedCVariadicType, ReturnTypesUseThinArrow,
 };
 use crate::parser::item::FrontMatterParsingMode;
 use crate::{exp, maybe_recover_from_interpolated_ty_qpath};
@@ -253,7 +253,27 @@ impl<'a> Parser<'a> {
     ) -> PResult<'a, P<Ty>> {
         let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes;
         maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery);
+        if self.token == token::Pound && self.look_ahead(1, |t| *t == token::OpenBracket) {
+            let attrs_wrapper = self.parse_outer_attributes()?;
+            let raw_attrs = attrs_wrapper.take_for_recovery(self.psess);
+            let attr_span = raw_attrs[0].span.to(raw_attrs.last().unwrap().span);
+            let (full_span, guar) = match self.parse_ty() {
+                Ok(ty) => {
+                    let full_span = attr_span.until(ty.span);
+                    let guar = self
+                        .dcx()
+                        .emit_err(AttributeOnType { span: attr_span, fix_span: full_span });
+                    (attr_span, guar)
+                }
+                Err(err) => {
+                    err.cancel();
+                    let guar = self.dcx().emit_err(AttributeOnEmptyType { span: attr_span });
+                    (attr_span, guar)
+                }
+            };
 
+            return Ok(self.mk_ty(full_span, TyKind::Err(guar)));
+        }
         if let Some(ty) = self.eat_metavar_seq_with_matcher(
             |mv_kind| matches!(mv_kind, MetaVarKind::Ty { .. }),
             |this| this.parse_ty_no_question_mark_recover(),
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 2663d5fe99c..890028d977d 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -324,6 +324,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 &Attribute::Parsed(AttributeKind::Coverage(attr_span, _)) => {
                     self.check_coverage(attr_span, span, target)
                 }
+                &Attribute::Parsed(AttributeKind::Coroutine(attr_span)) => {
+                    self.check_coroutine(attr_span, target)
+                }
                 Attribute::Unparsed(attr_item) => {
                     style = Some(attr_item.style);
                     match attr.path().as_slice() {
@@ -390,9 +393,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         [sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => {
                             self.check_autodiff(hir_id, attr, span, target)
                         }
-                        [sym::coroutine, ..] => {
-                            self.check_coroutine(attr, target);
-                        }
                         [sym::linkage, ..] => self.check_linkage(attr, span, target),
                         [
                             // ok
@@ -2651,11 +2651,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
     }
 
-    fn check_coroutine(&self, attr: &Attribute, target: Target) {
+    fn check_coroutine(&self, attr_span: Span, target: Target) {
         match target {
             Target::Closure => return,
             _ => {
-                self.dcx().emit_err(errors::CoroutineOnNonClosure { span: attr.span() });
+                self.dcx().emit_err(errors::CoroutineOnNonClosure { span: attr_span });
             }
         }
     }