From 0eb2adb7e877ceaa7d4919f7b881508ee507ec3b Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Fri, 26 Jan 2024 17:00:28 +0000
Subject: [PATCH 1/4] Add async bound modifier to enable async Fn bounds

---
 compiler/rustc_ast/src/ast.rs                 | 29 ++++++-
 compiler/rustc_ast_lowering/src/expr.rs       |  2 +
 compiler/rustc_ast_lowering/src/item.rs       | 13 ++-
 compiler/rustc_ast_lowering/src/lib.rs        | 19 ++--
 compiler/rustc_ast_lowering/src/path.rs       | 87 +++++++++++++++++--
 compiler/rustc_ast_pretty/src/pprust/state.rs | 21 +++--
 compiler/rustc_expand/src/build.rs            |  1 +
 compiler/rustc_parse/src/parser/ty.rs         | 16 ++--
 compiler/rustc_span/src/hygiene.rs            |  3 +
 compiler/rustc_span/src/symbol.rs             |  1 +
 tests/ui/async-await/async-fn/sugar.rs        | 14 +++
 tests/ui/stats/hir-stats.stderr               | 52 +++++------
 12 files changed, 199 insertions(+), 59 deletions(-)
 create mode 100644 tests/ui/async-await/async-fn/sugar.rs

diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 4eb8169d0c3..296a570de6b 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -291,12 +291,16 @@ pub use crate::node_id::{NodeId, CRATE_NODE_ID, DUMMY_NODE_ID};
 #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
 pub struct TraitBoundModifiers {
     pub constness: BoundConstness,
+    pub asyncness: BoundAsyncness,
     pub polarity: BoundPolarity,
 }
 
 impl TraitBoundModifiers {
-    pub const NONE: Self =
-        Self { constness: BoundConstness::Never, polarity: BoundPolarity::Positive };
+    pub const NONE: Self = Self {
+        constness: BoundConstness::Never,
+        asyncness: BoundAsyncness::Normal,
+        polarity: BoundPolarity::Positive,
+    };
 }
 
 /// The AST represents all type param bounds as types.
@@ -2562,6 +2566,25 @@ impl BoundConstness {
     }
 }
 
+/// The asyncness of a trait bound.
+#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
+#[derive(HashStable_Generic)]
+pub enum BoundAsyncness {
+    /// `Type: Trait`
+    Normal,
+    /// `Type: async Trait`
+    Async(Span),
+}
+
+impl BoundAsyncness {
+    pub fn as_str(self) -> &'static str {
+        match self {
+            Self::Normal => "",
+            Self::Async(_) => "async",
+        }
+    }
+}
+
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum FnRetTy {
     /// Returns type is not specified.
@@ -3300,7 +3323,7 @@ mod size_asserts {
     static_assert_size!(ForeignItem, 96);
     static_assert_size!(ForeignItemKind, 24);
     static_assert_size!(GenericArg, 24);
-    static_assert_size!(GenericBound, 72);
+    static_assert_size!(GenericBound, 88);
     static_assert_size!(Generics, 40);
     static_assert_size!(Impl, 136);
     static_assert_size!(Item, 136);
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 0ad4a59c17e..3b00a84e67e 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -100,6 +100,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         ParenthesizedGenericArgs::Err,
                         &ImplTraitContext::Disallowed(ImplTraitPosition::Path),
                         None,
+                        // Method calls can't have bound modifiers
+                        None,
                     ));
                     let receiver = self.lower_expr(receiver);
                     let args =
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index dd3f7289a60..0ff5d96cec2 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -343,14 +343,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let itctx = ImplTraitContext::Universal;
                 let (generics, (trait_ref, lowered_ty)) =
                     self.lower_generics(ast_generics, *constness, id, &itctx, |this| {
-                        let constness = match *constness {
-                            Const::Yes(span) => BoundConstness::Maybe(span),
-                            Const::No => BoundConstness::Never,
+                        let modifiers = TraitBoundModifiers {
+                            constness: match *constness {
+                                Const::Yes(span) => BoundConstness::Maybe(span),
+                                Const::No => BoundConstness::Never,
+                            },
+                            asyncness: BoundAsyncness::Normal,
+                            // we don't use this in bound lowering
+                            polarity: BoundPolarity::Positive,
                         };
 
                         let trait_ref = trait_ref.as_ref().map(|trait_ref| {
                             this.lower_trait_ref(
-                                constness,
+                                modifiers,
                                 trait_ref,
                                 &ImplTraitContext::Disallowed(ImplTraitPosition::Trait),
                             )
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 3621844efc8..7dfef9c07c5 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -131,6 +131,7 @@ struct LoweringContext<'a, 'hir> {
     allow_gen_future: Lrc<[Symbol]>,
     allow_async_iterator: Lrc<[Symbol]>,
     allow_for_await: Lrc<[Symbol]>,
+    allow_async_fn_traits: Lrc<[Symbol]>,
 
     /// Mapping from generics `def_id`s to TAIT generics `def_id`s.
     /// For each captured lifetime (e.g., 'a), we create a new lifetime parameter that is a generic
@@ -176,6 +177,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 [sym::gen_future].into()
             },
             allow_for_await: [sym::async_iterator].into(),
+            allow_async_fn_traits: [sym::async_fn_traits].into(),
             // FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller`
             // interact with `gen`/`async gen` blocks
             allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
@@ -1311,7 +1313,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         span: t.span,
                     },
                     itctx,
-                    ast::BoundConstness::Never,
+                    TraitBoundModifiers::NONE,
                 );
                 let bounds = this.arena.alloc_from_iter([bound]);
                 let lifetime_bound = this.elided_dyn_bound(t.span);
@@ -1426,7 +1428,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                                         itctx,
                                         // Still, don't pass along the constness here; we don't want to
                                         // synthesize any host effect args, it'd only cause problems.
-                                        ast::BoundConstness::Never,
+                                        TraitBoundModifiers {
+                                            constness: BoundConstness::Never,
+                                            ..*modifiers
+                                        },
                                     ))
                                 }
                                 BoundPolarity::Maybe(_) => None,
@@ -2019,7 +2024,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     ) -> hir::GenericBound<'hir> {
         match tpb {
             GenericBound::Trait(p, modifiers) => hir::GenericBound::Trait(
-                self.lower_poly_trait_ref(p, itctx, modifiers.constness.into()),
+                self.lower_poly_trait_ref(p, itctx, *modifiers),
                 self.lower_trait_bound_modifiers(*modifiers),
             ),
             GenericBound::Outlives(lifetime) => {
@@ -2192,7 +2197,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
     fn lower_trait_ref(
         &mut self,
-        constness: ast::BoundConstness,
+        modifiers: ast::TraitBoundModifiers,
         p: &TraitRef,
         itctx: &ImplTraitContext,
     ) -> hir::TraitRef<'hir> {
@@ -2202,7 +2207,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             &p.path,
             ParamMode::Explicit,
             itctx,
-            Some(constness),
+            Some(modifiers),
         ) {
             hir::QPath::Resolved(None, path) => path,
             qpath => panic!("lower_trait_ref: unexpected QPath `{qpath:?}`"),
@@ -2215,11 +2220,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         &mut self,
         p: &PolyTraitRef,
         itctx: &ImplTraitContext,
-        constness: ast::BoundConstness,
+        modifiers: ast::TraitBoundModifiers,
     ) -> hir::PolyTraitRef<'hir> {
         let bound_generic_params =
             self.lower_lifetime_binder(p.trait_ref.ref_id, &p.bound_generic_params);
-        let trait_ref = self.lower_trait_ref(constness, &p.trait_ref, itctx);
+        let trait_ref = self.lower_trait_ref(modifiers, &p.trait_ref, itctx);
         hir::PolyTraitRef { bound_generic_params, trait_ref, span: self.lower_span(p.span) }
     }
 
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
index c679ee56fcd..b4f1d7ce8c2 100644
--- a/compiler/rustc_ast_lowering/src/path.rs
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -6,12 +6,14 @@ use super::{GenericArgsCtor, LifetimeRes, ParenthesizedGenericArgs};
 use super::{ImplTraitContext, LoweringContext, ParamMode};
 
 use rustc_ast::{self as ast, *};
+use rustc_data_structures::sync::Lrc;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, PartialRes, Res};
+use rustc_hir::def_id::DefId;
 use rustc_hir::GenericArg;
 use rustc_middle::span_bug;
 use rustc_span::symbol::{kw, sym, Ident};
-use rustc_span::{BytePos, Span, DUMMY_SP};
+use rustc_span::{BytePos, DesugaringKind, Span, Symbol, DUMMY_SP};
 
 use smallvec::{smallvec, SmallVec};
 
@@ -24,8 +26,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         p: &Path,
         param_mode: ParamMode,
         itctx: &ImplTraitContext,
-        // constness of the impl/bound if this is a trait path
-        constness: Option<ast::BoundConstness>,
+        // modifiers of the impl/bound if this is a trait path
+        modifiers: Option<ast::TraitBoundModifiers>,
     ) -> hir::QPath<'hir> {
         let qself_position = qself.as_ref().map(|q| q.position);
         let qself = qself.as_ref().map(|q| self.lower_ty(&q.ty, itctx));
@@ -35,10 +37,27 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let base_res = partial_res.base_res();
         let unresolved_segments = partial_res.unresolved_segments();
 
+        let mut res = self.lower_res(base_res);
+
+        // When we have an `async` kw on a bound, map the trait it resolves to.
+        let mut bound_modifier_allowed_features = None;
+        if let Some(TraitBoundModifiers { asyncness: BoundAsyncness::Async(_), .. }) = modifiers {
+            if let Res::Def(DefKind::Trait, def_id) = res {
+                if let Some((async_def_id, features)) = self.map_trait_to_async_trait(def_id) {
+                    res = Res::Def(DefKind::Trait, async_def_id);
+                    bound_modifier_allowed_features = Some(features);
+                } else {
+                    panic!();
+                }
+            } else {
+                panic!();
+            }
+        }
+
         let path_span_lo = p.span.shrink_to_lo();
         let proj_start = p.segments.len() - unresolved_segments;
         let path = self.arena.alloc(hir::Path {
-            res: self.lower_res(base_res),
+            res,
             segments: self.arena.alloc_from_iter(p.segments[..proj_start].iter().enumerate().map(
                 |(i, segment)| {
                     let param_mode = match (qself_position, param_mode) {
@@ -77,7 +96,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         parenthesized_generic_args,
                         itctx,
                         // if this is the last segment, add constness to the trait path
-                        if i == proj_start - 1 { constness } else { None },
+                        if i == proj_start - 1 { modifiers.map(|m| m.constness) } else { None },
+                        bound_modifier_allowed_features.clone(),
                     )
                 },
             )),
@@ -88,6 +108,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             ),
         });
 
+        if let Some(bound_modifier_allowed_features) = bound_modifier_allowed_features {
+            path.span = self.mark_span_with_reason(
+                DesugaringKind::BoundModifier,
+                path.span,
+                Some(bound_modifier_allowed_features),
+            );
+        }
+
         // Simple case, either no projections, or only fully-qualified.
         // E.g., `std::mem::size_of` or `<I as Iterator>::Item`.
         if unresolved_segments == 0 {
@@ -125,6 +153,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 ParenthesizedGenericArgs::Err,
                 itctx,
                 None,
+                None,
             ));
             let qpath = hir::QPath::TypeRelative(ty, hir_segment);
 
@@ -166,6 +195,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     ParenthesizedGenericArgs::Err,
                     &ImplTraitContext::Disallowed(ImplTraitPosition::Path),
                     None,
+                    None,
                 )
             })),
             span: self.lower_span(p.span),
@@ -180,6 +210,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         parenthesized_generic_args: ParenthesizedGenericArgs,
         itctx: &ImplTraitContext,
         constness: Option<ast::BoundConstness>,
+        // Additional features ungated with a bound modifier like `async`.
+        // This is passed down to the implicit associated type binding in
+        // parenthesized bounds.
+        bound_modifier_allowed_features: Option<Lrc<[Symbol]>>,
     ) -> hir::PathSegment<'hir> {
         debug!("path_span: {:?}, lower_path_segment(segment: {:?})", path_span, segment);
         let (mut generic_args, infer_args) = if let Some(generic_args) = segment.args.as_deref() {
@@ -188,9 +222,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     self.lower_angle_bracketed_parameter_data(data, param_mode, itctx)
                 }
                 GenericArgs::Parenthesized(data) => match parenthesized_generic_args {
-                    ParenthesizedGenericArgs::ParenSugar => {
-                        self.lower_parenthesized_parameter_data(data, itctx)
-                    }
+                    ParenthesizedGenericArgs::ParenSugar => self
+                        .lower_parenthesized_parameter_data(
+                            data,
+                            itctx,
+                            bound_modifier_allowed_features,
+                        ),
                     ParenthesizedGenericArgs::Err => {
                         // Suggest replacing parentheses with angle brackets `Trait(params...)` to `Trait<params...>`
                         let sub = if !data.inputs.is_empty() {
@@ -357,6 +394,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         &mut self,
         data: &ParenthesizedArgs,
         itctx: &ImplTraitContext,
+        bound_modifier_allowed_features: Option<Lrc<[Symbol]>>,
     ) -> (GenericArgsCtor<'hir>, bool) {
         // Switch to `PassThrough` mode for anonymous lifetimes; this
         // means that we permit things like `&Ref<T>`, where `Ref` has
@@ -392,7 +430,19 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])),
         };
         let args = smallvec![GenericArg::Type(self.arena.alloc(self.ty_tup(*inputs_span, inputs)))];
-        let binding = self.assoc_ty_binding(sym::Output, output_ty.span, output_ty);
+
+        // If we have a bound like `async Fn() -> T`, make sure that we mark the
+        // `Output = T` associated type bound with the right feature gates.
+        let mut output_span = output_ty.span;
+        if let Some(bound_modifier_allowed_features) = bound_modifier_allowed_features {
+            output_span = self.mark_span_with_reason(
+                DesugaringKind::BoundModifier,
+                output_span,
+                Some(bound_modifier_allowed_features),
+            );
+        }
+        let binding = self.assoc_ty_binding(sym::Output, output_span, output_ty);
+
         (
             GenericArgsCtor {
                 args,
@@ -429,4 +479,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             kind,
         }
     }
+
+    /// When a bound is annotated with `async`, it signals to lowering that the trait
+    /// that the bound refers to should be mapped to the "async" flavor of the trait.
+    ///
+    /// This only needs to be done until we unify `AsyncFn` and `Fn` traits into one
+    /// that is generic over `async`ness, if that's ever possible, or modify the
+    /// lowering of `async Fn()` bounds to desugar to another trait like `LendingFn`.
+    fn map_trait_to_async_trait(&self, def_id: DefId) -> Option<(DefId, Lrc<[Symbol]>)> {
+        let lang_items = self.tcx.lang_items();
+        if Some(def_id) == lang_items.fn_trait() {
+            Some((lang_items.async_fn_trait()?, self.allow_async_fn_traits.clone()))
+        } else if Some(def_id) == lang_items.fn_mut_trait() {
+            Some((lang_items.async_fn_mut_trait()?, self.allow_async_fn_traits.clone()))
+        } else if Some(def_id) == lang_items.fn_once_trait() {
+            Some((lang_items.async_fn_once_trait()?, self.allow_async_fn_traits.clone()))
+        } else {
+            None
+        }
+    }
 }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index a6f6f0b29a0..731232bce65 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -8,6 +8,7 @@ mod item;
 use crate::pp::Breaks::{Consistent, Inconsistent};
 use crate::pp::{self, Breaks};
 use crate::pprust::state::expr::FixupContext;
+use ast::TraitBoundModifiers;
 use rustc_ast::attr::AttrIdGenerator;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, BinOpToken, CommentKind, Delimiter, Nonterminal, Token, TokenKind};
@@ -1590,18 +1591,28 @@ impl<'a> State<'a> {
             }
 
             match bound {
-                GenericBound::Trait(tref, modifier) => {
-                    match modifier.constness {
+                GenericBound::Trait(
+                    tref,
+                    TraitBoundModifiers { constness, asyncness, polarity },
+                ) => {
+                    match constness {
                         ast::BoundConstness::Never => {}
                         ast::BoundConstness::Always(_) | ast::BoundConstness::Maybe(_) => {
-                            self.word_space(modifier.constness.as_str());
+                            self.word_space(constness.as_str());
                         }
                     }
 
-                    match modifier.polarity {
+                    match asyncness {
+                        ast::BoundAsyncness::Normal => {}
+                        ast::BoundAsyncness::Async(_) => {
+                            self.word_space(asyncness.as_str());
+                        }
+                    }
+
+                    match polarity {
                         ast::BoundPolarity::Positive => {}
                         ast::BoundPolarity::Negative(_) | ast::BoundPolarity::Maybe(_) => {
-                            self.word(modifier.polarity.as_str());
+                            self.word(polarity.as_str());
                         }
                     }
 
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index f9bfebee12e..5879e025e7d 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -141,6 +141,7 @@ impl<'a> ExtCtxt<'a> {
                 } else {
                     ast::BoundConstness::Never
                 },
+                asyncness: ast::BoundAsyncness::Normal,
             },
         )
     }
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index a4fb92c67ac..72089dc2a91 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -8,14 +8,13 @@ use crate::errors::{
 };
 use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
 
-use ast::DUMMY_NODE_ID;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Delimiter, Token, TokenKind};
 use rustc_ast::util::case::Case;
 use rustc_ast::{
-    self as ast, BareFnTy, BoundConstness, BoundPolarity, FnRetTy, GenericBound, GenericBounds,
-    GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, PolyTraitRef,
-    TraitBoundModifiers, TraitObjectSyntax, Ty, TyKind,
+    self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, FnRetTy, GenericBound,
+    GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, PolyTraitRef,
+    TraitBoundModifiers, TraitObjectSyntax, Ty, TyKind, DUMMY_NODE_ID,
 };
 use rustc_errors::{Applicability, PResult};
 use rustc_span::symbol::{kw, sym, Ident};
@@ -880,6 +879,13 @@ impl<'a> Parser<'a> {
             BoundConstness::Never
         };
 
+        let asyncness = if self.token.span.at_least_rust_2018() && self.eat_keyword(kw::Async) {
+            self.sess.gated_spans.gate(sym::async_closure, self.prev_token.span);
+            BoundAsyncness::Async(self.prev_token.span)
+        } else {
+            BoundAsyncness::Normal
+        };
+
         let polarity = if self.eat(&token::Question) {
             BoundPolarity::Maybe(self.prev_token.span)
         } else if self.eat(&token::Not) {
@@ -889,7 +895,7 @@ impl<'a> Parser<'a> {
             BoundPolarity::Positive
         };
 
-        Ok(TraitBoundModifiers { constness, polarity })
+        Ok(TraitBoundModifiers { constness, asyncness, polarity })
     }
 
     /// Parses a type bound according to:
diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs
index 3d26efec5a6..527938daae4 100644
--- a/compiler/rustc_span/src/hygiene.rs
+++ b/compiler/rustc_span/src/hygiene.rs
@@ -1154,6 +1154,8 @@ pub enum DesugaringKind {
     Await,
     ForLoop,
     WhileLoop,
+    /// `async Fn()` bound modifier
+    BoundModifier,
 }
 
 impl DesugaringKind {
@@ -1169,6 +1171,7 @@ impl DesugaringKind {
             DesugaringKind::OpaqueTy => "`impl Trait`",
             DesugaringKind::ForLoop => "`for` loop",
             DesugaringKind::WhileLoop => "`while` loop",
+            DesugaringKind::BoundModifier => "trait bound modifier",
         }
     }
 }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index dbfc89c2d49..2dca9808ada 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -423,6 +423,7 @@ symbols! {
         async_fn_mut,
         async_fn_once,
         async_fn_track_caller,
+        async_fn_traits,
         async_for_loop,
         async_iterator,
         async_iterator_poll_next,
diff --git a/tests/ui/async-await/async-fn/sugar.rs b/tests/ui/async-await/async-fn/sugar.rs
new file mode 100644
index 00000000000..868fb799ae4
--- /dev/null
+++ b/tests/ui/async-await/async-fn/sugar.rs
@@ -0,0 +1,14 @@
+// edition: 2021
+// check-pass
+
+#![feature(async_closure)]
+
+async fn foo() {}
+
+async fn call_asyncly(f: impl async Fn(i32) -> i32) -> i32 {
+    f(1).await
+}
+
+fn main() {
+    let fut = call_asyncly(|x| async move { x + 1 });
+}
diff --git a/tests/ui/stats/hir-stats.stderr b/tests/ui/stats/hir-stats.stderr
index 8b9ec30db63..2c21c25d7c7 100644
--- a/tests/ui/stats/hir-stats.stderr
+++ b/tests/ui/stats/hir-stats.stderr
@@ -5,8 +5,8 @@ ast-stats-1 GenericArgs               40 ( 0.6%)             1            40
 ast-stats-1 - AngleBracketed            40 ( 0.6%)             1
 ast-stats-1 Crate                     40 ( 0.6%)             1            40
 ast-stats-1 ExprField                 48 ( 0.7%)             1            48
-ast-stats-1 WherePredicate            56 ( 0.9%)             1            56
-ast-stats-1 - BoundPredicate            56 ( 0.9%)             1
+ast-stats-1 WherePredicate            56 ( 0.8%)             1            56
+ast-stats-1 - BoundPredicate            56 ( 0.8%)             1
 ast-stats-1 Attribute                 64 ( 1.0%)             2            32
 ast-stats-1 - Normal                    32 ( 0.5%)             1
 ast-stats-1 - DocComment                32 ( 0.5%)             1
@@ -22,38 +22,38 @@ ast-stats-1 - MacCall                   32 ( 0.5%)             1
 ast-stats-1 - Expr                      96 ( 1.5%)             3
 ast-stats-1 Param                    160 ( 2.4%)             4            40
 ast-stats-1 Block                    192 ( 2.9%)             6            32
-ast-stats-1 Variant                  208 ( 3.2%)             2           104
-ast-stats-1 GenericBound             288 ( 4.4%)             4            72
-ast-stats-1 - Trait                    288 ( 4.4%)             4
-ast-stats-1 AssocItem                352 ( 5.4%)             4            88
+ast-stats-1 Variant                  208 ( 3.1%)             2           104
+ast-stats-1 GenericBound             352 ( 5.3%)             4            88
+ast-stats-1 - Trait                    352 ( 5.3%)             4
+ast-stats-1 AssocItem                352 ( 5.3%)             4            88
 ast-stats-1 - Type                     176 ( 2.7%)             2
 ast-stats-1 - Fn                       176 ( 2.7%)             2
 ast-stats-1 GenericParam             480 ( 7.3%)             5            96
-ast-stats-1 Pat                      504 ( 7.7%)             7            72
+ast-stats-1 Pat                      504 ( 7.6%)             7            72
 ast-stats-1 - Struct                    72 ( 1.1%)             1
 ast-stats-1 - Wild                      72 ( 1.1%)             1
-ast-stats-1 - Ident                    360 ( 5.5%)             5
-ast-stats-1 Expr                     576 ( 8.8%)             8            72
+ast-stats-1 - Ident                    360 ( 5.4%)             5
+ast-stats-1 Expr                     576 ( 8.7%)             8            72
 ast-stats-1 - Path                      72 ( 1.1%)             1
 ast-stats-1 - Match                     72 ( 1.1%)             1
 ast-stats-1 - Struct                    72 ( 1.1%)             1
 ast-stats-1 - Lit                      144 ( 2.2%)             2
 ast-stats-1 - Block                    216 ( 3.3%)             3
-ast-stats-1 PathSegment              720 (11.0%)            30            24
-ast-stats-1 Ty                       896 (13.7%)            14            64
+ast-stats-1 PathSegment              720 (10.9%)            30            24
+ast-stats-1 Ty                       896 (13.5%)            14            64
 ast-stats-1 - Ptr                       64 ( 1.0%)             1
 ast-stats-1 - Ref                       64 ( 1.0%)             1
-ast-stats-1 - ImplicitSelf             128 ( 2.0%)             2
-ast-stats-1 - Path                     640 ( 9.8%)            10
-ast-stats-1 Item                   1_224 (18.7%)             9           136
+ast-stats-1 - ImplicitSelf             128 ( 1.9%)             2
+ast-stats-1 - Path                     640 ( 9.7%)            10
+ast-stats-1 Item                   1_224 (18.5%)             9           136
 ast-stats-1 - Trait                    136 ( 2.1%)             1
 ast-stats-1 - Enum                     136 ( 2.1%)             1
 ast-stats-1 - ForeignMod               136 ( 2.1%)             1
 ast-stats-1 - Impl                     136 ( 2.1%)             1
-ast-stats-1 - Fn                       272 ( 4.2%)             2
+ast-stats-1 - Fn                       272 ( 4.1%)             2
 ast-stats-1 - Use                      408 ( 6.2%)             3
 ast-stats-1 ----------------------------------------------------------------
-ast-stats-1 Total                  6_552
+ast-stats-1 Total                  6_616
 ast-stats-1
 ast-stats-2 POST EXPANSION AST STATS
 ast-stats-2 Name                Accumulated Size         Count     Item Size
@@ -81,39 +81,39 @@ ast-stats-2 - Expr                      96 ( 1.3%)             3
 ast-stats-2 Param                    160 ( 2.2%)             4            40
 ast-stats-2 Block                    192 ( 2.7%)             6            32
 ast-stats-2 Variant                  208 ( 2.9%)             2           104
-ast-stats-2 GenericBound             288 ( 4.0%)             4            72
-ast-stats-2 - Trait                    288 ( 4.0%)             4
+ast-stats-2 GenericBound             352 ( 4.9%)             4            88
+ast-stats-2 - Trait                    352 ( 4.9%)             4
 ast-stats-2 AssocItem                352 ( 4.9%)             4            88
-ast-stats-2 - Type                     176 ( 2.5%)             2
-ast-stats-2 - Fn                       176 ( 2.5%)             2
+ast-stats-2 - Type                     176 ( 2.4%)             2
+ast-stats-2 - Fn                       176 ( 2.4%)             2
 ast-stats-2 GenericParam             480 ( 6.7%)             5            96
 ast-stats-2 Pat                      504 ( 7.0%)             7            72
 ast-stats-2 - Struct                    72 ( 1.0%)             1
 ast-stats-2 - Wild                      72 ( 1.0%)             1
 ast-stats-2 - Ident                    360 ( 5.0%)             5
-ast-stats-2 Expr                     648 ( 9.1%)             9            72
+ast-stats-2 Expr                     648 ( 9.0%)             9            72
 ast-stats-2 - Path                      72 ( 1.0%)             1
 ast-stats-2 - Match                     72 ( 1.0%)             1
 ast-stats-2 - Struct                    72 ( 1.0%)             1
 ast-stats-2 - InlineAsm                 72 ( 1.0%)             1
 ast-stats-2 - Lit                      144 ( 2.0%)             2
 ast-stats-2 - Block                    216 ( 3.0%)             3
-ast-stats-2 PathSegment              792 (11.1%)            33            24
-ast-stats-2 Ty                       896 (12.5%)            14            64
+ast-stats-2 PathSegment              792 (11.0%)            33            24
+ast-stats-2 Ty                       896 (12.4%)            14            64
 ast-stats-2 - Ptr                       64 ( 0.9%)             1
 ast-stats-2 - Ref                       64 ( 0.9%)             1
 ast-stats-2 - ImplicitSelf             128 ( 1.8%)             2
 ast-stats-2 - Path                     640 ( 8.9%)            10
-ast-stats-2 Item                   1_496 (20.9%)            11           136
+ast-stats-2 Item                   1_496 (20.7%)            11           136
 ast-stats-2 - Trait                    136 ( 1.9%)             1
 ast-stats-2 - Enum                     136 ( 1.9%)             1
 ast-stats-2 - ExternCrate              136 ( 1.9%)             1
 ast-stats-2 - ForeignMod               136 ( 1.9%)             1
 ast-stats-2 - Impl                     136 ( 1.9%)             1
 ast-stats-2 - Fn                       272 ( 3.8%)             2
-ast-stats-2 - Use                      544 ( 7.6%)             4
+ast-stats-2 - Use                      544 ( 7.5%)             4
 ast-stats-2 ----------------------------------------------------------------
-ast-stats-2 Total                  7_152
+ast-stats-2 Total                  7_216
 ast-stats-2
 hir-stats HIR STATS
 hir-stats Name                Accumulated Size         Count     Item Size

From cd2fd34ca68f701ade233a980093ee4444f7da3a Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Fri, 26 Jan 2024 17:00:34 +0000
Subject: [PATCH 2/4] Add tests

---
 tests/ui/async-await/async-fn/dyn-pos.rs      | 14 +++
 tests/ui/async-await/async-fn/dyn-pos.stderr  | 87 +++++++++++++++++++
 tests/ui/async-await/async-fn/edition-2015.rs |  8 ++
 .../async-await/async-fn/edition-2015.stderr  | 22 +++++
 tests/ui/async-await/async-fn/impl-header.rs  |  8 ++
 .../async-await/async-fn/impl-header.stderr   |  8 ++
 tests/ui/async-await/async-fn/impl-trait.rs   | 15 ++++
 .../async-await/async-fn/method-call-pos.rs   |  7 ++
 .../async-fn/method-call-pos.stderr           | 14 +++
 9 files changed, 183 insertions(+)
 create mode 100644 tests/ui/async-await/async-fn/dyn-pos.rs
 create mode 100644 tests/ui/async-await/async-fn/dyn-pos.stderr
 create mode 100644 tests/ui/async-await/async-fn/edition-2015.rs
 create mode 100644 tests/ui/async-await/async-fn/edition-2015.stderr
 create mode 100644 tests/ui/async-await/async-fn/impl-header.rs
 create mode 100644 tests/ui/async-await/async-fn/impl-header.stderr
 create mode 100644 tests/ui/async-await/async-fn/impl-trait.rs
 create mode 100644 tests/ui/async-await/async-fn/method-call-pos.rs
 create mode 100644 tests/ui/async-await/async-fn/method-call-pos.stderr

diff --git a/tests/ui/async-await/async-fn/dyn-pos.rs b/tests/ui/async-await/async-fn/dyn-pos.rs
new file mode 100644
index 00000000000..3201fb8dbf3
--- /dev/null
+++ b/tests/ui/async-await/async-fn/dyn-pos.rs
@@ -0,0 +1,14 @@
+// edition:2018
+
+#![feature(async_closure)]
+
+fn foo(x: &dyn async Fn()) {}
+//~^ ERROR the trait `AsyncFn` cannot be made into an object
+//~| ERROR the trait `AsyncFn` cannot be made into an object
+//~| ERROR the trait `AsyncFn` cannot be made into an object
+//~| ERROR the trait `AsyncFn` cannot be made into an object
+//~| ERROR the trait `AsyncFnMut` cannot be made into an object
+//~| ERROR the trait `AsyncFnMut` cannot be made into an object
+//~| ERROR the trait `AsyncFnMut` cannot be made into an object
+
+fn main() {}
diff --git a/tests/ui/async-await/async-fn/dyn-pos.stderr b/tests/ui/async-await/async-fn/dyn-pos.stderr
new file mode 100644
index 00000000000..c9323526516
--- /dev/null
+++ b/tests/ui/async-await/async-fn/dyn-pos.stderr
@@ -0,0 +1,87 @@
+error[E0038]: the trait `AsyncFn` cannot be made into an object
+  --> $DIR/dyn-pos.rs:5:16
+   |
+LL | fn foo(x: &dyn async Fn()) {}
+   |                ^^^^^^^^^^ `AsyncFn` cannot be made into an object
+   |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL
+   |
+   = note: the trait cannot be made into an object because it contains the generic associated type `CallFuture`
+
+error[E0038]: the trait `AsyncFnMut` cannot be made into an object
+  --> $DIR/dyn-pos.rs:5:16
+   |
+LL | fn foo(x: &dyn async Fn()) {}
+   |                ^^^^^^^^^^ `AsyncFnMut` cannot be made into an object
+   |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL
+   |
+   = note: the trait cannot be made into an object because it contains the generic associated type `CallMutFuture`
+
+error[E0038]: the trait `AsyncFn` cannot be made into an object
+  --> $DIR/dyn-pos.rs:5:16
+   |
+LL | fn foo(x: &dyn async Fn()) {}
+   |                ^^^^^^^^^^ `AsyncFn` cannot be made into an object
+   |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL
+   |
+   = note: the trait cannot be made into an object because it contains the generic associated type `CallFuture`
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0038]: the trait `AsyncFnMut` cannot be made into an object
+  --> $DIR/dyn-pos.rs:5:16
+   |
+LL | fn foo(x: &dyn async Fn()) {}
+   |                ^^^^^^^^^^ `AsyncFnMut` cannot be made into an object
+   |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL
+   |
+   = note: the trait cannot be made into an object because it contains the generic associated type `CallMutFuture`
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0038]: the trait `AsyncFn` cannot be made into an object
+  --> $DIR/dyn-pos.rs:5:16
+   |
+LL | fn foo(x: &dyn async Fn()) {}
+   |                ^^^^^^^^^^ `AsyncFn` cannot be made into an object
+   |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL
+   |
+   = note: the trait cannot be made into an object because it contains the generic associated type `CallFuture`
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0038]: the trait `AsyncFnMut` cannot be made into an object
+  --> $DIR/dyn-pos.rs:5:16
+   |
+LL | fn foo(x: &dyn async Fn()) {}
+   |                ^^^^^^^^^^ `AsyncFnMut` cannot be made into an object
+   |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL
+   |
+   = note: the trait cannot be made into an object because it contains the generic associated type `CallMutFuture`
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0038]: the trait `AsyncFn` cannot be made into an object
+  --> $DIR/dyn-pos.rs:5:12
+   |
+LL | fn foo(x: &dyn async Fn()) {}
+   |            ^^^^^^^^^^^^^^ `AsyncFn` cannot be made into an object
+   |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL
+   |
+   = note: the trait cannot be made into an object because it contains the generic associated type `CallFuture`
+  ::: $SRC_DIR/core/src/ops/async_function.rs:LL:COL
+   |
+   = note: the trait cannot be made into an object because it contains the generic associated type `CallMutFuture`
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0038`.
diff --git a/tests/ui/async-await/async-fn/edition-2015.rs b/tests/ui/async-await/async-fn/edition-2015.rs
new file mode 100644
index 00000000000..287d05ecacb
--- /dev/null
+++ b/tests/ui/async-await/async-fn/edition-2015.rs
@@ -0,0 +1,8 @@
+// FIXME(async_closures): This error message could be made better.
+
+fn foo(x: impl async Fn()) -> impl async Fn() {}
+//~^ ERROR expected
+//~| ERROR expected
+//~| ERROR expected
+
+fn main() {}
diff --git a/tests/ui/async-await/async-fn/edition-2015.stderr b/tests/ui/async-await/async-fn/edition-2015.stderr
new file mode 100644
index 00000000000..e79b92ccf9f
--- /dev/null
+++ b/tests/ui/async-await/async-fn/edition-2015.stderr
@@ -0,0 +1,22 @@
+error: expected one of `:` or `|`, found `)`
+  --> $DIR/edition-2015.rs:3:26
+   |
+LL | fn foo(x: impl async Fn()) -> impl async Fn() {}
+   |                          ^ expected one of `:` or `|`
+
+error: expected one of `(`, `)`, `+`, `,`, `::`, or `<`, found `Fn`
+  --> $DIR/edition-2015.rs:3:22
+   |
+LL | fn foo(x: impl async Fn()) -> impl async Fn() {}
+   |                     -^^ expected one of `(`, `)`, `+`, `,`, `::`, or `<`
+   |                     |
+   |                     help: missing `,`
+
+error: expected one of `(`, `+`, `::`, `<`, `where`, or `{`, found `Fn`
+  --> $DIR/edition-2015.rs:3:42
+   |
+LL | fn foo(x: impl async Fn()) -> impl async Fn() {}
+   |                                          ^^ expected one of `(`, `+`, `::`, `<`, `where`, or `{`
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/async-await/async-fn/impl-header.rs b/tests/ui/async-await/async-fn/impl-header.rs
new file mode 100644
index 00000000000..fb1844384ae
--- /dev/null
+++ b/tests/ui/async-await/async-fn/impl-header.rs
@@ -0,0 +1,8 @@
+// edition:2018
+
+struct F;
+
+impl async Fn<()> for F {}
+//~^ ERROR expected type, found keyword `async`
+
+fn main() {}
diff --git a/tests/ui/async-await/async-fn/impl-header.stderr b/tests/ui/async-await/async-fn/impl-header.stderr
new file mode 100644
index 00000000000..02cb4326242
--- /dev/null
+++ b/tests/ui/async-await/async-fn/impl-header.stderr
@@ -0,0 +1,8 @@
+error: expected type, found keyword `async`
+  --> $DIR/impl-header.rs:5:6
+   |
+LL | impl async Fn<()> for F {}
+   |      ^^^^^ expected type
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/async-await/async-fn/impl-trait.rs b/tests/ui/async-await/async-fn/impl-trait.rs
new file mode 100644
index 00000000000..97f6696fe14
--- /dev/null
+++ b/tests/ui/async-await/async-fn/impl-trait.rs
@@ -0,0 +1,15 @@
+// edition:2018
+// check-pass
+
+#![feature(async_closure, type_alias_impl_trait)]
+
+type Tait = impl async Fn();
+fn tait() -> Tait {
+    || async {}
+}
+
+fn foo(x: impl async Fn()) -> impl async Fn() { x }
+
+fn param<T: async Fn()>() {}
+
+fn main() {}
diff --git a/tests/ui/async-await/async-fn/method-call-pos.rs b/tests/ui/async-await/async-fn/method-call-pos.rs
new file mode 100644
index 00000000000..aaa0245b986
--- /dev/null
+++ b/tests/ui/async-await/async-fn/method-call-pos.rs
@@ -0,0 +1,7 @@
+// edition:2018
+
+fn main() {
+    <_ as async Fn()>(|| async {});
+    //~^ ERROR expected identifier, found keyword `async`
+    //~| ERROR expected one of
+}
diff --git a/tests/ui/async-await/async-fn/method-call-pos.stderr b/tests/ui/async-await/async-fn/method-call-pos.stderr
new file mode 100644
index 00000000000..527515a1b44
--- /dev/null
+++ b/tests/ui/async-await/async-fn/method-call-pos.stderr
@@ -0,0 +1,14 @@
+error: expected identifier, found keyword `async`
+  --> $DIR/method-call-pos.rs:4:11
+   |
+LL |     <_ as async Fn()>(|| async {});
+   |           ^^^^^ expected identifier, found keyword
+
+error: expected one of `(`, `::`, `<`, or `>`, found `Fn`
+  --> $DIR/method-call-pos.rs:4:17
+   |
+LL |     <_ as async Fn()>(|| async {});
+   |                 ^^ expected one of `(`, `::`, `<`, or `>`
+
+error: aborting due to 2 previous errors
+

From 54db272cc972f232cc50a7c6dff30140f904738a Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Fri, 26 Jan 2024 17:15:43 +0000
Subject: [PATCH 3/4] Better error message in ed 2015

---
 compiler/rustc_parse/messages.ftl             |  2 +
 compiler/rustc_parse/src/errors.rs            |  9 ++++
 compiler/rustc_parse/src/parser/ty.rs         | 15 +++++-
 .../async-fn/edition-2015-not-async-bound.rs  | 10 ++++
 tests/ui/async-await/async-fn/edition-2015.rs | 11 ++--
 .../async-await/async-fn/edition-2015.stderr  | 53 +++++++++++++------
 6 files changed, 76 insertions(+), 24 deletions(-)
 create mode 100644 tests/ui/async-await/async-fn/edition-2015-not-async-bound.rs

diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index aac8c0b3103..7c2ecf34c17 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -22,6 +22,8 @@ parse_associated_static_item_not_allowed = associated `static` items are not all
 
 parse_async_block_in_2015 = `async` blocks are only allowed in Rust 2018 or later
 
+parse_async_bound_modifier_in_2015 = `async` trait bounds are only allowed in Rust 2018 or later
+
 parse_async_fn_in_2015 = `async fn` is not permitted in Rust 2015
     .label = to use `async fn`, switch to Rust 2018 or later
 
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 4e4bf9bdad9..86a64d90deb 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -1588,6 +1588,15 @@ pub(crate) struct AsyncMoveBlockIn2015 {
     pub span: Span,
 }
 
+#[derive(Diagnostic)]
+#[diag(parse_async_bound_modifier_in_2015)]
+pub(crate) struct AsyncBoundModifierIn2015 {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub help: HelpUseLatestEdition,
+}
+
 #[derive(Diagnostic)]
 #[diag(parse_self_argument_pointer)]
 pub(crate) struct SelfArgumentPointer {
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 72089dc2a91..5fe54a536a7 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -3,8 +3,8 @@ use super::{Parser, PathStyle, TokenType};
 use crate::errors::{
     self, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType,
     FnPointerCannotBeAsync, FnPointerCannotBeConst, FnPtrWithGenerics, FnPtrWithGenericsSugg,
-    InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, NestedCVariadicType,
-    ReturnTypesUseThinArrow,
+    HelpUseLatestEdition, InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime,
+    NestedCVariadicType, ReturnTypesUseThinArrow,
 };
 use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
 
@@ -882,6 +882,17 @@ impl<'a> Parser<'a> {
         let asyncness = if self.token.span.at_least_rust_2018() && self.eat_keyword(kw::Async) {
             self.sess.gated_spans.gate(sym::async_closure, self.prev_token.span);
             BoundAsyncness::Async(self.prev_token.span)
+        } else if self.may_recover()
+            && self.token.span.is_rust_2015()
+            && self.is_kw_followed_by_ident(kw::Async)
+        {
+            self.bump(); // eat `async`
+            self.dcx().emit_err(errors::AsyncBoundModifierIn2015 {
+                span: self.prev_token.span,
+                help: HelpUseLatestEdition::new(),
+            });
+            self.sess.gated_spans.gate(sym::async_closure, self.prev_token.span);
+            BoundAsyncness::Async(self.prev_token.span)
         } else {
             BoundAsyncness::Normal
         };
diff --git a/tests/ui/async-await/async-fn/edition-2015-not-async-bound.rs b/tests/ui/async-await/async-fn/edition-2015-not-async-bound.rs
new file mode 100644
index 00000000000..6436787b665
--- /dev/null
+++ b/tests/ui/async-await/async-fn/edition-2015-not-async-bound.rs
@@ -0,0 +1,10 @@
+// check-pass
+// Make sure that we don't eagerly recover `async ::Bound` in edition 2015.
+
+mod async {
+    pub trait Foo {}
+}
+
+fn test(x: impl async ::Foo) {}
+
+fn main() {}
diff --git a/tests/ui/async-await/async-fn/edition-2015.rs b/tests/ui/async-await/async-fn/edition-2015.rs
index 287d05ecacb..83b9d415dda 100644
--- a/tests/ui/async-await/async-fn/edition-2015.rs
+++ b/tests/ui/async-await/async-fn/edition-2015.rs
@@ -1,8 +1,7 @@
-// FIXME(async_closures): This error message could be made better.
-
-fn foo(x: impl async Fn()) -> impl async Fn() {}
-//~^ ERROR expected
-//~| ERROR expected
-//~| ERROR expected
+fn foo(x: impl async Fn()) -> impl async Fn() { x }
+//~^ ERROR `async` trait bounds are only allowed in Rust 2018 or later
+//~| ERROR `async` trait bounds are only allowed in Rust 2018 or later
+//~| ERROR async closures are unstable
+//~| ERROR async closures are unstable
 
 fn main() {}
diff --git a/tests/ui/async-await/async-fn/edition-2015.stderr b/tests/ui/async-await/async-fn/edition-2015.stderr
index e79b92ccf9f..0029d53868d 100644
--- a/tests/ui/async-await/async-fn/edition-2015.stderr
+++ b/tests/ui/async-await/async-fn/edition-2015.stderr
@@ -1,22 +1,43 @@
-error: expected one of `:` or `|`, found `)`
-  --> $DIR/edition-2015.rs:3:26
+error: `async` trait bounds are only allowed in Rust 2018 or later
+  --> $DIR/edition-2015.rs:1:16
    |
-LL | fn foo(x: impl async Fn()) -> impl async Fn() {}
-   |                          ^ expected one of `:` or `|`
-
-error: expected one of `(`, `)`, `+`, `,`, `::`, or `<`, found `Fn`
-  --> $DIR/edition-2015.rs:3:22
+LL | fn foo(x: impl async Fn()) -> impl async Fn() { x }
+   |                ^^^^^
    |
-LL | fn foo(x: impl async Fn()) -> impl async Fn() {}
-   |                     -^^ expected one of `(`, `)`, `+`, `,`, `::`, or `<`
-   |                     |
-   |                     help: missing `,`
+   = help: pass `--edition 2021` to `rustc`
+   = note: for more on editions, read https://doc.rust-lang.org/edition-guide
 
-error: expected one of `(`, `+`, `::`, `<`, `where`, or `{`, found `Fn`
-  --> $DIR/edition-2015.rs:3:42
+error: `async` trait bounds are only allowed in Rust 2018 or later
+  --> $DIR/edition-2015.rs:1:36
    |
-LL | fn foo(x: impl async Fn()) -> impl async Fn() {}
-   |                                          ^^ expected one of `(`, `+`, `::`, `<`, `where`, or `{`
+LL | fn foo(x: impl async Fn()) -> impl async Fn() { x }
+   |                                    ^^^^^
+   |
+   = help: pass `--edition 2021` to `rustc`
+   = note: for more on editions, read https://doc.rust-lang.org/edition-guide
 
-error: aborting due to 3 previous errors
+error[E0658]: async closures are unstable
+  --> $DIR/edition-2015.rs:1:16
+   |
+LL | fn foo(x: impl async Fn()) -> impl async Fn() { x }
+   |                ^^^^^
+   |
+   = note: see issue #62290 <https://github.com/rust-lang/rust/issues/62290> for more information
+   = help: add `#![feature(async_closure)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = help: to use an async block, remove the `||`: `async {`
 
+error[E0658]: async closures are unstable
+  --> $DIR/edition-2015.rs:1:36
+   |
+LL | fn foo(x: impl async Fn()) -> impl async Fn() { x }
+   |                                    ^^^^^
+   |
+   = note: see issue #62290 <https://github.com/rust-lang/rust/issues/62290> for more information
+   = help: add `#![feature(async_closure)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = help: to use an async block, remove the `||`: `async {`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0658`.

From 3913c9a0cacf4bacb4ca1c6255271d54ec995f02 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Fri, 26 Jan 2024 17:33:42 +0000
Subject: [PATCH 4/4] Error on incorrect item kind in async bound

---
 compiler/rustc_ast_lowering/messages.ftl      |  6 ++++
 compiler/rustc_ast_lowering/src/errors.rs     | 15 ++++++++++
 compiler/rustc_ast_lowering/src/path.rs       | 29 +++++++++++++------
 tests/ui/async-await/async-fn/not-a-trait.rs  | 13 +++++++++
 .../async-await/async-fn/not-a-trait.stderr   | 16 ++++++++++
 tests/ui/async-await/async-fn/wrong-trait.rs  | 10 +++++++
 .../async-await/async-fn/wrong-trait.stderr   |  8 +++++
 7 files changed, 88 insertions(+), 9 deletions(-)
 create mode 100644 tests/ui/async-await/async-fn/not-a-trait.rs
 create mode 100644 tests/ui/async-await/async-fn/not-a-trait.stderr
 create mode 100644 tests/ui/async-await/async-fn/wrong-trait.rs
 create mode 100644 tests/ui/async-await/async-fn/wrong-trait.stderr

diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl
index 8615016cda5..e0dc227ca4c 100644
--- a/compiler/rustc_ast_lowering/messages.ftl
+++ b/compiler/rustc_ast_lowering/messages.ftl
@@ -11,6 +11,12 @@ ast_lowering_argument = argument
 ast_lowering_assoc_ty_parentheses =
     parenthesized generic arguments cannot be used in associated type constraints
 
+ast_lowering_async_bound_not_on_trait =
+    `async` bound modifier only allowed on trait, not `{$descr}`
+
+ast_lowering_async_bound_only_for_fn_traits =
+    `async` bound modifier only allowed on `Fn`/`FnMut`/`FnOnce` traits
+
 ast_lowering_async_coroutines_not_supported =
     `async` coroutines are not yet supported
 
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index 51bb8a96fad..7658dfa5d5f 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -395,3 +395,18 @@ pub(crate) struct GenericParamDefaultInBinder {
     #[primary_span]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(ast_lowering_async_bound_not_on_trait)]
+pub(crate) struct AsyncBoundNotOnTrait {
+    #[primary_span]
+    pub span: Span,
+    pub descr: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_lowering_async_bound_only_for_fn_traits)]
+pub(crate) struct AsyncBoundOnlyForFnTraits {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
index b4f1d7ce8c2..b58ac5c3dae 100644
--- a/compiler/rustc_ast_lowering/src/path.rs
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -1,6 +1,8 @@
 use crate::ImplTraitPosition;
 
-use super::errors::{GenericTypeWithParentheses, UseAngleBrackets};
+use super::errors::{
+    AsyncBoundNotOnTrait, AsyncBoundOnlyForFnTraits, GenericTypeWithParentheses, UseAngleBrackets,
+};
 use super::ResolverAstLoweringExt;
 use super::{GenericArgsCtor, LifetimeRes, ParenthesizedGenericArgs};
 use super::{ImplTraitContext, LoweringContext, ParamMode};
@@ -42,15 +44,24 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // When we have an `async` kw on a bound, map the trait it resolves to.
         let mut bound_modifier_allowed_features = None;
         if let Some(TraitBoundModifiers { asyncness: BoundAsyncness::Async(_), .. }) = modifiers {
-            if let Res::Def(DefKind::Trait, def_id) = res {
-                if let Some((async_def_id, features)) = self.map_trait_to_async_trait(def_id) {
-                    res = Res::Def(DefKind::Trait, async_def_id);
-                    bound_modifier_allowed_features = Some(features);
-                } else {
-                    panic!();
+            match res {
+                Res::Def(DefKind::Trait, def_id) => {
+                    if let Some((async_def_id, features)) = self.map_trait_to_async_trait(def_id) {
+                        res = Res::Def(DefKind::Trait, async_def_id);
+                        bound_modifier_allowed_features = Some(features);
+                    } else {
+                        self.dcx().emit_err(AsyncBoundOnlyForFnTraits { span: p.span });
+                    }
+                }
+                Res::Err => {
+                    // No additional error.
+                }
+                _ => {
+                    // This error isn't actually emitted AFAICT, but it's best to keep
+                    // it around in case the resolver doesn't always check the defkind
+                    // of an item or something.
+                    self.dcx().emit_err(AsyncBoundNotOnTrait { span: p.span, descr: res.descr() });
                 }
-            } else {
-                panic!();
             }
         }
 
diff --git a/tests/ui/async-await/async-fn/not-a-trait.rs b/tests/ui/async-await/async-fn/not-a-trait.rs
new file mode 100644
index 00000000000..a8fa21e5568
--- /dev/null
+++ b/tests/ui/async-await/async-fn/not-a-trait.rs
@@ -0,0 +1,13 @@
+// edition:2018
+
+#![feature(async_closure)]
+
+struct S;
+
+fn test(x: impl async S) {}
+//~^ ERROR expected trait, found struct `S`
+
+fn missing(x: impl async Missing) {}
+//~^ ERROR cannot find trait `Missing` in this scope
+
+fn main() {}
diff --git a/tests/ui/async-await/async-fn/not-a-trait.stderr b/tests/ui/async-await/async-fn/not-a-trait.stderr
new file mode 100644
index 00000000000..d49f7d7d2fc
--- /dev/null
+++ b/tests/ui/async-await/async-fn/not-a-trait.stderr
@@ -0,0 +1,16 @@
+error[E0404]: expected trait, found struct `S`
+  --> $DIR/not-a-trait.rs:7:23
+   |
+LL | fn test(x: impl async S) {}
+   |                       ^ not a trait
+
+error[E0405]: cannot find trait `Missing` in this scope
+  --> $DIR/not-a-trait.rs:10:26
+   |
+LL | fn missing(x: impl async Missing) {}
+   |                          ^^^^^^^ not found in this scope
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0404, E0405.
+For more information about an error, try `rustc --explain E0404`.
diff --git a/tests/ui/async-await/async-fn/wrong-trait.rs b/tests/ui/async-await/async-fn/wrong-trait.rs
new file mode 100644
index 00000000000..c431a362b1e
--- /dev/null
+++ b/tests/ui/async-await/async-fn/wrong-trait.rs
@@ -0,0 +1,10 @@
+// edition:2018
+
+#![feature(async_closure)]
+
+trait Foo {}
+
+fn test(x: impl async Foo) {}
+//~^ ERROR `async` bound modifier only allowed on `Fn`/`FnMut`/`FnOnce` traits
+
+fn main() {}
diff --git a/tests/ui/async-await/async-fn/wrong-trait.stderr b/tests/ui/async-await/async-fn/wrong-trait.stderr
new file mode 100644
index 00000000000..b39f5aa623c
--- /dev/null
+++ b/tests/ui/async-await/async-fn/wrong-trait.stderr
@@ -0,0 +1,8 @@
+error: `async` bound modifier only allowed on `Fn`/`FnMut`/`FnOnce` traits
+  --> $DIR/wrong-trait.rs:7:23
+   |
+LL | fn test(x: impl async Foo) {}
+   |                       ^^^
+
+error: aborting due to 1 previous error
+