about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMaybe Waffle <waffle.lapkin@gmail.com>2022-06-02 20:15:05 +0400
committerMaybe Waffle <waffle.lapkin@gmail.com>2022-07-12 16:25:16 +0400
commit40ae7b5b8e09da657b62bc849b8bcdf99a1cb210 (patch)
treef2a08c2651b0cb786a37697ee7806b272c01bb1d
parentfbdb07f4e7f4666085aec4b1ed2fd05817dc42cf (diff)
downloadrust-40ae7b5b8e09da657b62bc849b8bcdf99a1cb210.tar.gz
rust-40ae7b5b8e09da657b62bc849b8bcdf99a1cb210.zip
Parse closure binders
This is first step in implementing RFC 3216.
- Parse `for<'a>` before closures in ast
  - Error in lowering
- Add `closure_lifetime_binder` feature
-rw-r--r--compiler/rustc_ast/src/ast.rs27
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs18
-rw-r--r--compiler/rustc_ast/src/visit.rs25
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs24
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs4
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs5
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs11
-rw-r--r--compiler/rustc_builtin_macros/src/assert/context.rs2
-rw-r--r--compiler/rustc_expand/src/build.rs1
-rw-r--r--compiler/rustc_feature/src/active.rs2
-rw-r--r--compiler/rustc_lint/src/early.rs2
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs48
-rw-r--r--compiler/rustc_resolve/src/def_collector.rs2
-rw-r--r--compiler/rustc_resolve/src/late.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--src/test/ui-fulldeps/pprust-expr-roundtrip.rs1
-rw-r--r--src/test/ui/closures/binder/async-closure-with-binder.rs7
-rw-r--r--src/test/ui/closures/binder/async-closure-with-binder.stderr8
-rw-r--r--src/test/ui/closures/binder/implicit-return.rs6
-rw-r--r--src/test/ui/closures/binder/implicit-return.stderr10
-rw-r--r--src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.rs7
-rw-r--r--src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.stderr33
-rw-r--r--src/test/ui/feature-gates/feature-gate-closure_lifetime_binder.rs12
-rw-r--r--src/test/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr67
24 files changed, 287 insertions, 38 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index f705d004422..ac2328a5824 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -1390,7 +1390,7 @@ pub enum ExprKind {
     /// A closure (e.g., `move |a, b, c| a + b + c`).
     ///
     /// The final span is the span of the argument block `|...|`.
-    Closure(CaptureBy, Async, Movability, P<FnDecl>, P<Expr>, Span),
+    Closure(ClosureBinder, CaptureBy, Async, Movability, P<FnDecl>, P<Expr>, Span),
     /// A block (`'label: { ... }`).
     Block(P<Block>, Option<Label>),
     /// An async block (`async move { ... }`).
@@ -1518,6 +1518,31 @@ pub enum Movability {
     Movable,
 }
 
+/// Closure lifetime binder, `for<'a, 'b>` in `for<'a, 'b> |_: &'a (), _: &'b ()|`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum ClosureBinder {
+    /// The binder is not present, all closure lifetimes are inferred.
+    NotPresent,
+    /// The binder is present.
+    For {
+        /// Span of the whole `for<>` clause
+        ///
+        /// ```text
+        /// for<'a, 'b> |_: &'a (), _: &'b ()| { ... }
+        /// ^^^^^^^^^^^ -- this
+        /// ```
+        span: Span,
+
+        /// Lifetimes in the `for<>` closure
+        ///
+        /// ```text
+        /// for<'a, 'b> |_: &'a (), _: &'b ()| { ... }
+        ///     ^^^^^^ -- this
+        /// ```
+        generic_params: P<[GenericParam]>,
+    },
+}
+
 /// Represents a macro invocation. The `path` indicates which macro
 /// is being invoked, and the `args` are arguments passed to it.
 #[derive(Clone, Encodable, Decodable, Debug)]
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 85bb5296486..d933ea2da9e 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -125,6 +125,10 @@ pub trait MutVisitor: Sized {
         noop_visit_asyncness(a, self);
     }
 
+    fn visit_closure_binder(&mut self, b: &mut ClosureBinder) {
+        noop_visit_closure_binder(b, self);
+    }
+
     fn visit_block(&mut self, b: &mut P<Block>) {
         noop_visit_block(b, self);
     }
@@ -825,6 +829,17 @@ pub fn visit_constness<T: MutVisitor>(constness: &mut Const, vis: &mut T) {
     }
 }
 
+pub fn noop_visit_closure_binder<T: MutVisitor>(binder: &mut ClosureBinder, vis: &mut T) {
+    match binder {
+        ClosureBinder::NotPresent => {}
+        ClosureBinder::For { span: _, generic_params } => {
+            let mut vec = std::mem::take(generic_params).into_vec();
+            vec.flat_map_in_place(|param| vis.flat_map_generic_param(param));
+            *generic_params = P::from_vec(vec);
+        }
+    }
+}
+
 pub fn noop_visit_asyncness<T: MutVisitor>(asyncness: &mut Async, vis: &mut T) {
     match asyncness {
         Async::Yes { span: _, closure_id, return_impl_trait_id } => {
@@ -1336,7 +1351,8 @@ pub fn noop_visit_expr<T: MutVisitor>(
             vis.visit_expr(expr);
             arms.flat_map_in_place(|arm| vis.flat_map_arm(arm));
         }
-        ExprKind::Closure(_capture_by, asyncness, _movability, decl, body, span) => {
+        ExprKind::Closure(binder, _capture_by, asyncness, _movability, decl, body, span) => {
+            vis.visit_closure_binder(binder);
             vis.visit_asyncness(asyncness);
             vis.visit_fn_decl(decl);
             vis.visit_expr(body);
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 2ce8590d771..327fc505e99 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -56,14 +56,14 @@ pub enum FnKind<'a> {
     Fn(FnCtxt, Ident, &'a FnSig, &'a Visibility, &'a Generics, Option<&'a Block>),
 
     /// E.g., `|x, y| body`.
-    Closure(&'a FnDecl, &'a Expr),
+    Closure(&'a ClosureBinder, &'a FnDecl, &'a Expr),
 }
 
 impl<'a> FnKind<'a> {
     pub fn header(&self) -> Option<&'a FnHeader> {
         match *self {
             FnKind::Fn(_, _, sig, _, _, _) => Some(&sig.header),
-            FnKind::Closure(_, _) => None,
+            FnKind::Closure(_, _, _) => None,
         }
     }
 
@@ -77,7 +77,7 @@ impl<'a> FnKind<'a> {
     pub fn decl(&self) -> &'a FnDecl {
         match self {
             FnKind::Fn(_, _, sig, _, _, _) => &sig.decl,
-            FnKind::Closure(decl, _) => decl,
+            FnKind::Closure(_, decl, _) => decl,
         }
     }
 
@@ -155,6 +155,9 @@ pub trait Visitor<'ast>: Sized {
     fn visit_generics(&mut self, g: &'ast Generics) {
         walk_generics(self, g)
     }
+    fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) {
+        walk_closure_binder(self, b)
+    }
     fn visit_where_predicate(&mut self, p: &'ast WherePredicate) {
         walk_where_predicate(self, p)
     }
@@ -636,6 +639,15 @@ pub fn walk_generics<'a, V: Visitor<'a>>(visitor: &mut V, generics: &'a Generics
     walk_list!(visitor, visit_where_predicate, &generics.where_clause.predicates);
 }
 
+pub fn walk_closure_binder<'a, V: Visitor<'a>>(visitor: &mut V, binder: &'a ClosureBinder) {
+    match binder {
+        ClosureBinder::NotPresent => {}
+        ClosureBinder::For { span: _, generic_params } => {
+            walk_list!(visitor, visit_generic_param, generic_params)
+        }
+    }
+}
+
 pub fn walk_where_predicate<'a, V: Visitor<'a>>(visitor: &mut V, predicate: &'a WherePredicate) {
     match *predicate {
         WherePredicate::BoundPredicate(WhereBoundPredicate {
@@ -682,7 +694,8 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>, _span: Spa
             walk_fn_decl(visitor, &sig.decl);
             walk_list!(visitor, visit_block, body);
         }
-        FnKind::Closure(decl, body) => {
+        FnKind::Closure(binder, decl, body) => {
+            visitor.visit_closure_binder(binder);
             walk_fn_decl(visitor, decl);
             visitor.visit_expr(body);
         }
@@ -856,8 +869,8 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
             visitor.visit_expr(subexpression);
             walk_list!(visitor, visit_arm, arms);
         }
-        ExprKind::Closure(_, _, _, ref decl, ref body, _decl_span) => {
-            visitor.visit_fn(FnKind::Closure(decl, body), expression.span, expression.id)
+        ExprKind::Closure(ref binder, _, _, _, ref decl, ref body, _decl_span) => {
+            visitor.visit_fn(FnKind::Closure(binder, decl, body), expression.span, expression.id)
         }
         ExprKind::Block(ref block, ref opt_label) => {
             walk_list!(visitor, visit_label, opt_label);
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 9e02e7ed3b9..7a35660b0af 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -155,6 +155,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     self.lower_expr_await(span, expr)
                 }
                 ExprKind::Closure(
+                    ref binder,
                     capture_clause,
                     asyncness,
                     movability,
@@ -164,6 +165,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 ) => {
                     if let Async::Yes { closure_id, .. } = asyncness {
                         self.lower_expr_async_closure(
+                            binder,
                             capture_clause,
                             e.id,
                             closure_id,
@@ -173,6 +175,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         )
                     } else {
                         self.lower_expr_closure(
+                            binder,
                             capture_clause,
                             e.id,
                             movability,
@@ -831,6 +834,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
     fn lower_expr_closure(
         &mut self,
+        binder: &ClosureBinder,
         capture_clause: CaptureBy,
         closure_id: NodeId,
         movability: Movability,
@@ -838,6 +842,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
         body: &Expr,
         fn_decl_span: Span,
     ) -> hir::ExprKind<'hir> {
+        // FIXME(waffle): lower binder
+        if let &ClosureBinder::For { span, .. } = binder {
+            self.sess
+                .struct_span_err(span, "`for<...>` binders for closures are not yet supported")
+                .help("consider removing `for<...>`")
+                .emit();
+        }
+
         let (body, generator_option) = self.with_new_scopes(move |this| {
             let prev = this.current_item;
             this.current_item = Some(fn_decl_span);
@@ -908,6 +920,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
     fn lower_expr_async_closure(
         &mut self,
+        binder: &ClosureBinder,
         capture_clause: CaptureBy,
         closure_id: NodeId,
         inner_closure_id: NodeId,
@@ -915,6 +928,17 @@ impl<'hir> LoweringContext<'_, 'hir> {
         body: &Expr,
         fn_decl_span: Span,
     ) -> hir::ExprKind<'hir> {
+        // FIXME(waffle): lower binder
+        if let &ClosureBinder::For { span, .. } = binder {
+            self.sess
+                .struct_span_err(
+                    span,
+                    "`for<...>` binders for async closures are not yet supported",
+                )
+                .help("consider removing `for<...>`")
+                .emit();
+        }
+
         let outer_decl =
             FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };
 
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 3942062656f..f284bf4650a 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -1597,6 +1597,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 .emit();
         }
 
+        if let FnKind::Closure(ClosureBinder::For { generic_params, .. }, ..) = fk {
+            self.check_late_bound_lifetime_defs(generic_params);
+        }
+
         if let FnKind::Fn(
             _,
             _,
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index fd2dd6cf6c7..e69f85eacf7 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -744,6 +744,11 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
         "async closures are unstable",
         "to use an async block, remove the `||`: `async {`"
     );
+    gate_all!(
+        closure_lifetime_binder,
+        "`for<...>` binders for closures are experimental",
+        "consider removing `for<...>`"
+    );
     gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental");
     gate_all!(generators, "yield syntax is experimental");
     gate_all!(raw_ref_op, "raw address of syntax is experimental");
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 9f44f1b6cc2..ead38caee28 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -389,6 +389,7 @@ impl<'a> State<'a> {
                 self.bclose(expr.span, empty);
             }
             ast::ExprKind::Closure(
+                ref binder,
                 capture_clause,
                 asyncness,
                 movability,
@@ -396,6 +397,7 @@ impl<'a> State<'a> {
                 ref body,
                 _,
             ) => {
+                self.print_closure_binder(binder);
                 self.print_movability(movability);
                 self.print_asyncness(asyncness);
                 self.print_capture_clause(capture_clause);
@@ -594,6 +596,15 @@ impl<'a> State<'a> {
         self.end(); // Close enclosing cbox.
     }
 
+    fn print_closure_binder(&mut self, binder: &ast::ClosureBinder) {
+        match binder {
+            ast::ClosureBinder::NotPresent => {}
+            ast::ClosureBinder::For { generic_params, .. } => {
+                self.print_formal_generic_params(&generic_params)
+            }
+        }
+    }
+
     fn print_movability(&mut self, movability: ast::Movability) {
         match movability {
             ast::Movability::Static => self.word_space("static"),
diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs
index 9e50d33486c..01152ff7df5 100644
--- a/compiler/rustc_builtin_macros/src/assert/context.rs
+++ b/compiler/rustc_builtin_macros/src/assert/context.rs
@@ -294,7 +294,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
             | ExprKind::Block(_, _)
             | ExprKind::Box(_)
             | ExprKind::Break(_, _)
-            | ExprKind::Closure(_, _, _, _, _, _)
+            | ExprKind::Closure(_, _, _, _, _, _, _)
             | ExprKind::ConstBlock(_)
             | ExprKind::Continue(_)
             | ExprKind::Err
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index 74e9bbeeeaf..fa3e2a4a5b8 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -520,6 +520,7 @@ impl<'a> ExtCtxt<'a> {
         self.expr(
             span,
             ast::ExprKind::Closure(
+                ast::ClosureBinder::NotPresent,
                 ast::CaptureBy::Ref,
                 ast::Async::No,
                 ast::Movability::Movable,
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index b54f0ef361a..8d19ae2e3f4 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -331,6 +331,8 @@ declare_features! (
     (active, cfg_target_thread_local, "1.7.0", Some(29594), None),
     /// Allow conditional compilation depending on rust version
     (active, cfg_version, "1.45.0", Some(64796), None),
+    /// Allows `for<...>` on closures and generators.
+    (active, closure_lifetime_binder, "1.64.0", Some(97362), None),
     /// Allows `#[track_caller]` on closures and generators.
     (active, closure_track_caller, "1.57.0", Some(87417), None),
     /// Allows to use the `#[cmse_nonsecure_entry]` attribute.
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index 5de35dc0856..6c9638400b2 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -218,7 +218,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
         // Explicitly check for lints associated with 'closure_id', since
         // it does not have a corresponding AST node
         match e.kind {
-            ast::ExprKind::Closure(_, ast::Async::Yes { closure_id, .. }, ..)
+            ast::ExprKind::Closure(_, _, ast::Async::Yes { closure_id, .. }, ..)
             | ast::ExprKind::Async(_, closure_id, ..) => self.check_id(closure_id),
             _ => {}
         }
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 2c43563b104..c793e734224 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -15,10 +15,10 @@ use rustc_ast::util::classify;
 use rustc_ast::util::literal::LitError;
 use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity};
 use rustc_ast::visit::Visitor;
-use rustc_ast::StmtKind;
 use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, Lit, UnOp, DUMMY_NODE_ID};
 use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind};
 use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
+use rustc_ast::{ClosureBinder, StmtKind};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::thin_vec::ThinVec;
 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, PResult};
@@ -1343,11 +1343,7 @@ impl<'a> Parser<'a> {
             self.parse_if_expr(attrs)
         } else if self.check_keyword(kw::For) {
             if self.choose_generics_over_qpath(1) {
-                // NOTE(Centril, eddyb): DO NOT REMOVE! Beyond providing parser recovery,
-                // this is an insurance policy in case we allow qpaths in (tuple-)struct patterns.
-                // When `for <Foo as Bar>::Proj in $expr $block` is wanted,
-                // you can disambiguate in favor of a pattern with `(...)`.
-                self.recover_quantified_closure_expr(attrs)
+                self.parse_closure_expr(attrs)
             } else {
                 assert!(self.eat_keyword(kw::For));
                 self.parse_for_expr(None, self.prev_token.span, attrs)
@@ -2094,29 +2090,21 @@ impl<'a> Parser<'a> {
         Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new()))
     }
 
-    /// Recover on an explicitly quantified closure expression, e.g., `for<'a> |x: &'a u8| *x + 1`.
-    fn recover_quantified_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+    /// Parses a closure expression (e.g., `move |args| expr`).
+    fn parse_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
         let lo = self.token.span;
-        let _ = self.parse_late_bound_lifetime_defs()?;
-        let span_for = lo.to(self.prev_token.span);
-        let closure = self.parse_closure_expr(attrs)?;
 
-        self.struct_span_err(span_for, "cannot introduce explicit parameters for a closure")
-            .span_label(closure.span, "the parameters are attached to this closure")
-            .span_suggestion(
-                span_for,
-                "remove the parameters",
-                "",
-                Applicability::MachineApplicable,
-            )
-            .emit();
+        let binder = if self.check_keyword(kw::For) {
+            let lo = self.token.span;
+            let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
+            let span = lo.to(self.prev_token.span);
 
-        Ok(self.mk_expr_err(lo.to(closure.span)))
-    }
+            self.sess.gated_spans.gate(sym::closure_lifetime_binder, span);
 
-    /// Parses a closure expression (e.g., `move |args| expr`).
-    fn parse_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
-        let lo = self.token.span;
+            ClosureBinder::For { span, generic_params: P::from_vec(lifetime_defs) }
+        } else {
+            ClosureBinder::NotPresent
+        };
 
         let movability =
             if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable };
@@ -2160,7 +2148,15 @@ impl<'a> Parser<'a> {
 
         let closure = self.mk_expr(
             lo.to(body.span),
-            ExprKind::Closure(capture_clause, asyncness, movability, decl, body, lo.to(decl_hi)),
+            ExprKind::Closure(
+                binder,
+                capture_clause,
+                asyncness,
+                movability,
+                decl,
+                body,
+                lo.to(decl_hi),
+            ),
             attrs,
         );
 
diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs
index 52706fbb9e6..66641fb2cb2 100644
--- a/compiler/rustc_resolve/src/def_collector.rs
+++ b/compiler/rustc_resolve/src/def_collector.rs
@@ -259,7 +259,7 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> {
     fn visit_expr(&mut self, expr: &'a Expr) {
         let parent_def = match expr.kind {
             ExprKind::MacCall(..) => return self.visit_macro_invoc(expr.id),
-            ExprKind::Closure(_, asyncness, ..) => {
+            ExprKind::Closure(_, _, asyncness, ..) => {
                 // Async closures desugar to closures inside of closures, so
                 // we must create two defs.
                 let closure_def = self.create_def(expr.id, DefPathData::ClosureExpr, expr.span);
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 640d13ea435..caf6f8c8392 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -3495,7 +3495,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             // `async |x| ...` gets desugared to `|x| future_from_generator(|| ...)`, so we need to
             // resolve the arguments within the proper scopes so that usages of them inside the
             // closure are detected as upvars rather than normal closure arg usages.
-            ExprKind::Closure(_, Async::Yes { .. }, _, ref fn_decl, ref body, _span) => {
+            ExprKind::Closure(_, _, Async::Yes { .. }, _, ref fn_decl, ref body, _span) => {
                 self.with_rib(ValueNS, NormalRibKind, |this| {
                     this.with_label_rib(ClosureOrAsyncRibKind, |this| {
                         // Resolve arguments:
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 9b6967621f1..4505dd64a10 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -459,6 +459,7 @@ symbols! {
         clone_closures,
         clone_from,
         closure,
+        closure_lifetime_binder,
         closure_to_fn_coercion,
         closure_track_caller,
         cmp,
diff --git a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
index a37d3a32571..a679b7b4e19 100644
--- a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
+++ b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
@@ -114,6 +114,7 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) {
                 let decl = P(FnDecl { inputs: vec![], output: FnRetTy::Default(DUMMY_SP) });
                 iter_exprs(depth - 1, &mut |e| {
                     g(ExprKind::Closure(
+                        ClosureBinder::NotPresent,
                         CaptureBy::Value,
                         Async::No,
                         Movability::Movable,
diff --git a/src/test/ui/closures/binder/async-closure-with-binder.rs b/src/test/ui/closures/binder/async-closure-with-binder.rs
new file mode 100644
index 00000000000..b9ecbd9e5d6
--- /dev/null
+++ b/src/test/ui/closures/binder/async-closure-with-binder.rs
@@ -0,0 +1,7 @@
+// edition:2021
+#![feature(closure_lifetime_binder)]
+#![feature(async_closure)]
+fn main() {
+    for<'a> async || ();
+    //~^ ERROR `for<...>` binders on `async` closures are not currently supported
+}
diff --git a/src/test/ui/closures/binder/async-closure-with-binder.stderr b/src/test/ui/closures/binder/async-closure-with-binder.stderr
new file mode 100644
index 00000000000..46420a17752
--- /dev/null
+++ b/src/test/ui/closures/binder/async-closure-with-binder.stderr
@@ -0,0 +1,8 @@
+error: `for<...>` binders on `async` closures are not currently supported
+  --> $DIR/async-closure-with-binder.rs:5:5
+   |
+LL |     for<'a> async || ();
+   |     ^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/closures/binder/implicit-return.rs b/src/test/ui/closures/binder/implicit-return.rs
new file mode 100644
index 00000000000..3aefbdcb299
--- /dev/null
+++ b/src/test/ui/closures/binder/implicit-return.rs
@@ -0,0 +1,6 @@
+#![feature(closure_lifetime_binder)]
+
+fn main() {
+    let _f = for<'a> |_: &'a ()| {};
+    //~^ implicit return type is forbidden when `for<...>` is present
+}
diff --git a/src/test/ui/closures/binder/implicit-return.stderr b/src/test/ui/closures/binder/implicit-return.stderr
new file mode 100644
index 00000000000..022a4e027b6
--- /dev/null
+++ b/src/test/ui/closures/binder/implicit-return.stderr
@@ -0,0 +1,10 @@
+error: implicit return type is forbidden when `for<...>` is present
+  --> $DIR/implicit-return.rs:4:34
+   |
+LL |     let _f = for<'a> |_: &'a ()| {};
+   |              -------             ^
+   |              |
+   |              `for<...>` is here
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.rs b/src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.rs
new file mode 100644
index 00000000000..b476dd50cc9
--- /dev/null
+++ b/src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.rs
@@ -0,0 +1,7 @@
+#![feature(closure_lifetime_binder)]
+fn main() {
+    for<> |_: &'a ()| -> () {};
+    //~^ ERROR use of undeclared lifetime name `'a`
+    for<'a> |_: &'b ()| -> () {};
+    //~^ ERROR use of undeclared lifetime name `'b`
+}
diff --git a/src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.stderr b/src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.stderr
new file mode 100644
index 00000000000..1381acc15ca
--- /dev/null
+++ b/src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.stderr
@@ -0,0 +1,33 @@
+error[E0261]: use of undeclared lifetime name `'a`
+  --> $DIR/suggestion-for-introducing-lifetime-into-binder.rs:3:16
+   |
+LL |     for<> |_: &'a ()| -> () {};
+   |                ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'a` here
+   |
+LL |     for<'a, > |_: &'a ()| -> () {};
+   |         +++
+help: consider introducing lifetime `'a` here
+   |
+LL | fn main<'a>() {
+   |        ++++
+
+error[E0261]: use of undeclared lifetime name `'b`
+  --> $DIR/suggestion-for-introducing-lifetime-into-binder.rs:5:18
+   |
+LL |     for<'a> |_: &'b ()| -> () {};
+   |                  ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'b` here
+   |
+LL |     for<'b, 'a> |_: &'b ()| -> () {};
+   |         +++
+help: consider introducing lifetime `'b` here
+   |
+LL | fn main<'b>() {
+   |        ++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0261`.
diff --git a/src/test/ui/feature-gates/feature-gate-closure_lifetime_binder.rs b/src/test/ui/feature-gates/feature-gate-closure_lifetime_binder.rs
new file mode 100644
index 00000000000..1e2090186a6
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-closure_lifetime_binder.rs
@@ -0,0 +1,12 @@
+fn main() {
+    for<> || {};
+    //~^ ERROR `for<...>` binders for closures are experimental
+    //~^^ ERROR `for<...>` binders for closures are not yet supported
+    for<'a> || {};
+    //~^ ERROR `for<...>` binders for closures are experimental
+    //~^^ ERROR `for<...>` binders for closures are not yet supported
+    for<'a, 'b> |_: &'a ()| {};
+    //~^ ERROR `for<...>` binders for closures are experimental
+    //~^^ ERROR `for<...>` binders for closures are not yet supported
+    //~^^^ ERROR use of undeclared lifetime name `'a`
+}
diff --git a/src/test/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr b/src/test/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr
new file mode 100644
index 00000000000..77ce6f9f2d6
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr
@@ -0,0 +1,67 @@
+error[E0261]: use of undeclared lifetime name `'a`
+  --> $DIR/feature-gate-closure_lifetime_binder.rs:8:22
+   |
+LL | fn main() {
+   |        - help: consider introducing lifetime `'a` here: `<'a>`
+...
+LL |     for<'a, 'b> |_: &'a ()| {};
+   |                      ^^ undeclared lifetime
+
+error[E0658]: `for<...>` binders for closures are experimental
+  --> $DIR/feature-gate-closure_lifetime_binder.rs:2:5
+   |
+LL |     for<> || {};
+   |     ^^^^^
+   |
+   = note: see issue #97362 <https://github.com/rust-lang/rust/issues/97362> for more information
+   = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable
+   = help: consider removing `for<...>`
+
+error[E0658]: `for<...>` binders for closures are experimental
+  --> $DIR/feature-gate-closure_lifetime_binder.rs:5:5
+   |
+LL |     for<'a> || {};
+   |     ^^^^^^^
+   |
+   = note: see issue #97362 <https://github.com/rust-lang/rust/issues/97362> for more information
+   = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable
+   = help: consider removing `for<...>`
+
+error[E0658]: `for<...>` binders for closures are experimental
+  --> $DIR/feature-gate-closure_lifetime_binder.rs:8:5
+   |
+LL |     for<'a, 'b> |_: &'a ()| {};
+   |     ^^^^^^^^^^^
+   |
+   = note: see issue #97362 <https://github.com/rust-lang/rust/issues/97362> for more information
+   = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable
+   = help: consider removing `for<...>`
+
+error: `for<...>` binders for closures are not yet supported
+  --> $DIR/feature-gate-closure_lifetime_binder.rs:2:5
+   |
+LL |     for<> || {};
+   |     ^^^^^
+   |
+   = help: consider removing `for<...>`
+
+error: `for<...>` binders for closures are not yet supported
+  --> $DIR/feature-gate-closure_lifetime_binder.rs:5:5
+   |
+LL |     for<'a> || {};
+   |     ^^^^^^^
+   |
+   = help: consider removing `for<...>`
+
+error: `for<...>` binders for closures are not yet supported
+  --> $DIR/feature-gate-closure_lifetime_binder.rs:8:5
+   |
+LL |     for<'a, 'b> |_: &'a ()| {};
+   |     ^^^^^^^^^^^
+   |
+   = help: consider removing `for<...>`
+
+error: aborting due to 7 previous errors
+
+Some errors have detailed explanations: E0261, E0658.
+For more information about an error, try `rustc --explain E0261`.