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
This commit is contained in:
Maybe Waffle 2022-06-02 20:15:05 +04:00
parent fbdb07f4e7
commit 40ae7b5b8e
24 changed files with 288 additions and 39 deletions

View File

@ -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)]

View File

@ -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);

View File

@ -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);

View File

@ -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) };

View File

@ -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(
_,
_,

View File

@ -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");

View File

@ -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"),

View File

@ -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

View File

@ -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,

View File

@ -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.

View File

@ -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),
_ => {}
}

View File

@ -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,30 +2090,22 @@ 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>> {
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();
Ok(self.mk_expr_err(lo.to(closure.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;
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);
self.sess.gated_spans.gate(sym::closure_lifetime_binder, 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,
);

View File

@ -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);

View File

@ -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:

View File

@ -459,6 +459,7 @@ symbols! {
clone_closures,
clone_from,
closure,
closure_lifetime_binder,
closure_to_fn_coercion,
closure_track_caller,
cmp,

View File

@ -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,

View File

@ -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
}

View File

@ -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

View File

@ -0,0 +1,6 @@
#![feature(closure_lifetime_binder)]
fn main() {
let _f = for<'a> |_: &'a ()| {};
//~^ implicit return type is forbidden when `for<...>` is present
}

View File

@ -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

View File

@ -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`
}

View File

@ -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`.

View File

@ -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`
}

View File

@ -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`.