Skip to content

Commit fa8f684

Browse files
authored
Rollup merge of rust-lang#98705 - WaffleLapkin:closure_binder, r=cjgillot
Implement `for<>` lifetime binder for closures This PR implements RFC 3216 ([TI](rust-lang#97362)) and allows code like the following: ```rust let _f = for<'a, 'b> |a: &'a A, b: &'b B| -> &'b C { b.c(a) }; // ^^^^^^^^^^^--- new! ``` cc `@Aaron1011` `@cjgillot`
2 parents be44888 + 9aa142b commit fa8f684

File tree

91 files changed

+840
-205
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+840
-205
lines changed

compiler/rustc_ast/src/ast.rs

+26-1
Original file line numberDiff line numberDiff line change
@@ -1390,7 +1390,7 @@ pub enum ExprKind {
13901390
/// A closure (e.g., `move |a, b, c| a + b + c`).
13911391
///
13921392
/// The final span is the span of the argument block `|...|`.
1393-
Closure(CaptureBy, Async, Movability, P<FnDecl>, P<Expr>, Span),
1393+
Closure(ClosureBinder, CaptureBy, Async, Movability, P<FnDecl>, P<Expr>, Span),
13941394
/// A block (`'label: { ... }`).
13951395
Block(P<Block>, Option<Label>),
13961396
/// An async block (`async move { ... }`).
@@ -1518,6 +1518,31 @@ pub enum Movability {
15181518
Movable,
15191519
}
15201520

1521+
/// Closure lifetime binder, `for<'a, 'b>` in `for<'a, 'b> |_: &'a (), _: &'b ()|`.
1522+
#[derive(Clone, Encodable, Decodable, Debug)]
1523+
pub enum ClosureBinder {
1524+
/// The binder is not present, all closure lifetimes are inferred.
1525+
NotPresent,
1526+
/// The binder is present.
1527+
For {
1528+
/// Span of the whole `for<>` clause
1529+
///
1530+
/// ```text
1531+
/// for<'a, 'b> |_: &'a (), _: &'b ()| { ... }
1532+
/// ^^^^^^^^^^^ -- this
1533+
/// ```
1534+
span: Span,
1535+
1536+
/// Lifetimes in the `for<>` closure
1537+
///
1538+
/// ```text
1539+
/// for<'a, 'b> |_: &'a (), _: &'b ()| { ... }
1540+
/// ^^^^^^ -- this
1541+
/// ```
1542+
generic_params: P<[GenericParam]>,
1543+
},
1544+
}
1545+
15211546
/// Represents a macro invocation. The `path` indicates which macro
15221547
/// is being invoked, and the `args` are arguments passed to it.
15231548
#[derive(Clone, Encodable, Decodable, Debug)]

compiler/rustc_ast/src/mut_visit.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ pub trait MutVisitor: Sized {
125125
noop_visit_asyncness(a, self);
126126
}
127127

128+
fn visit_closure_binder(&mut self, b: &mut ClosureBinder) {
129+
noop_visit_closure_binder(b, self);
130+
}
131+
128132
fn visit_block(&mut self, b: &mut P<Block>) {
129133
noop_visit_block(b, self);
130134
}
@@ -825,6 +829,17 @@ pub fn visit_constness<T: MutVisitor>(constness: &mut Const, vis: &mut T) {
825829
}
826830
}
827831

832+
pub fn noop_visit_closure_binder<T: MutVisitor>(binder: &mut ClosureBinder, vis: &mut T) {
833+
match binder {
834+
ClosureBinder::NotPresent => {}
835+
ClosureBinder::For { span: _, generic_params } => {
836+
let mut vec = std::mem::take(generic_params).into_vec();
837+
vec.flat_map_in_place(|param| vis.flat_map_generic_param(param));
838+
*generic_params = P::from_vec(vec);
839+
}
840+
}
841+
}
842+
828843
pub fn noop_visit_asyncness<T: MutVisitor>(asyncness: &mut Async, vis: &mut T) {
829844
match asyncness {
830845
Async::Yes { span: _, closure_id, return_impl_trait_id } => {
@@ -1336,7 +1351,8 @@ pub fn noop_visit_expr<T: MutVisitor>(
13361351
vis.visit_expr(expr);
13371352
arms.flat_map_in_place(|arm| vis.flat_map_arm(arm));
13381353
}
1339-
ExprKind::Closure(_capture_by, asyncness, _movability, decl, body, span) => {
1354+
ExprKind::Closure(binder, _capture_by, asyncness, _movability, decl, body, span) => {
1355+
vis.visit_closure_binder(binder);
13401356
vis.visit_asyncness(asyncness);
13411357
vis.visit_fn_decl(decl);
13421358
vis.visit_expr(body);

compiler/rustc_ast/src/visit.rs

+19-6
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,14 @@ pub enum FnKind<'a> {
5656
Fn(FnCtxt, Ident, &'a FnSig, &'a Visibility, &'a Generics, Option<&'a Block>),
5757

5858
/// E.g., `|x, y| body`.
59-
Closure(&'a FnDecl, &'a Expr),
59+
Closure(&'a ClosureBinder, &'a FnDecl, &'a Expr),
6060
}
6161

6262
impl<'a> FnKind<'a> {
6363
pub fn header(&self) -> Option<&'a FnHeader> {
6464
match *self {
6565
FnKind::Fn(_, _, sig, _, _, _) => Some(&sig.header),
66-
FnKind::Closure(_, _) => None,
66+
FnKind::Closure(_, _, _) => None,
6767
}
6868
}
6969

@@ -77,7 +77,7 @@ impl<'a> FnKind<'a> {
7777
pub fn decl(&self) -> &'a FnDecl {
7878
match self {
7979
FnKind::Fn(_, _, sig, _, _, _) => &sig.decl,
80-
FnKind::Closure(decl, _) => decl,
80+
FnKind::Closure(_, decl, _) => decl,
8181
}
8282
}
8383

@@ -155,6 +155,9 @@ pub trait Visitor<'ast>: Sized {
155155
fn visit_generics(&mut self, g: &'ast Generics) {
156156
walk_generics(self, g)
157157
}
158+
fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) {
159+
walk_closure_binder(self, b)
160+
}
158161
fn visit_where_predicate(&mut self, p: &'ast WherePredicate) {
159162
walk_where_predicate(self, p)
160163
}
@@ -636,6 +639,15 @@ pub fn walk_generics<'a, V: Visitor<'a>>(visitor: &mut V, generics: &'a Generics
636639
walk_list!(visitor, visit_where_predicate, &generics.where_clause.predicates);
637640
}
638641

642+
pub fn walk_closure_binder<'a, V: Visitor<'a>>(visitor: &mut V, binder: &'a ClosureBinder) {
643+
match binder {
644+
ClosureBinder::NotPresent => {}
645+
ClosureBinder::For { generic_params, span: _ } => {
646+
walk_list!(visitor, visit_generic_param, generic_params)
647+
}
648+
}
649+
}
650+
639651
pub fn walk_where_predicate<'a, V: Visitor<'a>>(visitor: &mut V, predicate: &'a WherePredicate) {
640652
match *predicate {
641653
WherePredicate::BoundPredicate(WhereBoundPredicate {
@@ -682,7 +694,8 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>, _span: Spa
682694
walk_fn_decl(visitor, &sig.decl);
683695
walk_list!(visitor, visit_block, body);
684696
}
685-
FnKind::Closure(decl, body) => {
697+
FnKind::Closure(binder, decl, body) => {
698+
visitor.visit_closure_binder(binder);
686699
walk_fn_decl(visitor, decl);
687700
visitor.visit_expr(body);
688701
}
@@ -856,8 +869,8 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
856869
visitor.visit_expr(subexpression);
857870
walk_list!(visitor, visit_arm, arms);
858871
}
859-
ExprKind::Closure(_, _, _, ref decl, ref body, _decl_span) => {
860-
visitor.visit_fn(FnKind::Closure(decl, body), expression.span, expression.id)
872+
ExprKind::Closure(ref binder, _, _, _, ref decl, ref body, _decl_span) => {
873+
visitor.visit_fn(FnKind::Closure(binder, decl, body), expression.span, expression.id)
861874
}
862875
ExprKind::Block(ref block, ref opt_label) => {
863876
walk_list!(visitor, visit_label, opt_label);

compiler/rustc_ast_lowering/src/expr.rs

+56-15
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
155155
self.lower_expr_await(span, expr)
156156
}
157157
ExprKind::Closure(
158+
ref binder,
158159
capture_clause,
159160
asyncness,
160161
movability,
@@ -164,6 +165,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
164165
) => {
165166
if let Async::Yes { closure_id, .. } = asyncness {
166167
self.lower_expr_async_closure(
168+
binder,
167169
capture_clause,
168170
e.id,
169171
closure_id,
@@ -173,6 +175,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
173175
)
174176
} else {
175177
self.lower_expr_closure(
178+
binder,
176179
capture_clause,
177180
e.id,
178181
movability,
@@ -605,13 +608,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
605608
});
606609

607610
// `static |_task_context| -> <ret_ty> { body }`:
608-
let generator_kind = hir::ExprKind::Closure {
609-
capture_clause,
610-
bound_generic_params: &[],
611-
fn_decl,
612-
body,
613-
fn_decl_span: self.lower_span(span),
614-
movability: Some(hir::Movability::Static),
611+
let generator_kind = {
612+
let c = self.arena.alloc(hir::Closure {
613+
binder: hir::ClosureBinder::Default,
614+
capture_clause,
615+
bound_generic_params: &[],
616+
fn_decl,
617+
body,
618+
fn_decl_span: self.lower_span(span),
619+
movability: Some(hir::Movability::Static),
620+
});
621+
622+
hir::ExprKind::Closure(c)
615623
};
616624
let generator = hir::Expr {
617625
hir_id: self.lower_node_id(closure_node_id),
@@ -831,14 +839,17 @@ impl<'hir> LoweringContext<'_, 'hir> {
831839

832840
fn lower_expr_closure(
833841
&mut self,
842+
binder: &ClosureBinder,
834843
capture_clause: CaptureBy,
835844
closure_id: NodeId,
836845
movability: Movability,
837846
decl: &FnDecl,
838847
body: &Expr,
839848
fn_decl_span: Span,
840849
) -> hir::ExprKind<'hir> {
841-
let (body, generator_option) = self.with_new_scopes(move |this| {
850+
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
851+
852+
let (body_id, generator_option) = self.with_new_scopes(move |this| {
842853
let prev = this.current_item;
843854
this.current_item = Some(fn_decl_span);
844855
let mut generator_kind = None;
@@ -853,18 +864,21 @@ impl<'hir> LoweringContext<'_, 'hir> {
853864
(body_id, generator_option)
854865
});
855866

856-
self.with_lifetime_binder(closure_id, &[], |this, bound_generic_params| {
867+
self.with_lifetime_binder(closure_id, generic_params, |this, bound_generic_params| {
857868
// Lower outside new scope to preserve `is_in_loop_condition`.
858869
let fn_decl = this.lower_fn_decl(decl, None, FnDeclKind::Closure, None);
859870

860-
hir::ExprKind::Closure {
871+
let c = self.arena.alloc(hir::Closure {
872+
binder: binder_clause,
861873
capture_clause,
862874
bound_generic_params,
863875
fn_decl,
864-
body,
876+
body: body_id,
865877
fn_decl_span: this.lower_span(fn_decl_span),
866878
movability: generator_option,
867-
}
879+
});
880+
881+
hir::ExprKind::Closure(c)
868882
})
869883
}
870884

@@ -906,15 +920,40 @@ impl<'hir> LoweringContext<'_, 'hir> {
906920
}
907921
}
908922

923+
fn lower_closure_binder<'c>(
924+
&mut self,
925+
binder: &'c ClosureBinder,
926+
) -> (hir::ClosureBinder, &'c [GenericParam]) {
927+
let (binder, params) = match binder {
928+
ClosureBinder::NotPresent => (hir::ClosureBinder::Default, &[][..]),
929+
&ClosureBinder::For { span, ref generic_params } => {
930+
let span = self.lower_span(span);
931+
(hir::ClosureBinder::For { span }, &**generic_params)
932+
}
933+
};
934+
935+
(binder, params)
936+
}
937+
909938
fn lower_expr_async_closure(
910939
&mut self,
940+
binder: &ClosureBinder,
911941
capture_clause: CaptureBy,
912942
closure_id: NodeId,
913943
inner_closure_id: NodeId,
914944
decl: &FnDecl,
915945
body: &Expr,
916946
fn_decl_span: Span,
917947
) -> hir::ExprKind<'hir> {
948+
if let &ClosureBinder::For { span, .. } = binder {
949+
self.tcx.sess.span_err(
950+
span,
951+
"`for<...>` binders on `async` closures are not currently supported",
952+
);
953+
}
954+
955+
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
956+
918957
let outer_decl =
919958
FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };
920959

@@ -952,20 +991,22 @@ impl<'hir> LoweringContext<'_, 'hir> {
952991
body_id
953992
});
954993

955-
self.with_lifetime_binder(closure_id, &[], |this, bound_generic_params| {
994+
self.with_lifetime_binder(closure_id, generic_params, |this, bound_generic_params| {
956995
// We need to lower the declaration outside the new scope, because we
957996
// have to conserve the state of being inside a loop condition for the
958997
// closure argument types.
959998
let fn_decl = this.lower_fn_decl(&outer_decl, None, FnDeclKind::Closure, None);
960999

961-
hir::ExprKind::Closure {
1000+
let c = self.arena.alloc(hir::Closure {
1001+
binder: binder_clause,
9621002
capture_clause,
9631003
bound_generic_params,
9641004
fn_decl,
9651005
body,
9661006
fn_decl_span: this.lower_span(fn_decl_span),
9671007
movability: None,
968-
}
1008+
});
1009+
hir::ExprKind::Closure(c)
9691010
})
9701011
}
9711012

compiler/rustc_ast_passes/src/ast_validation.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1597,6 +1597,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
15971597
.emit();
15981598
}
15991599

1600+
if let FnKind::Closure(ClosureBinder::For { generic_params, .. }, ..) = fk {
1601+
self.check_late_bound_lifetime_defs(generic_params);
1602+
}
1603+
16001604
if let FnKind::Fn(
16011605
_,
16021606
_,

compiler/rustc_ast_passes/src/feature_gate.rs

+5
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,11 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
744744
"async closures are unstable",
745745
"to use an async block, remove the `||`: `async {`"
746746
);
747+
gate_all!(
748+
closure_lifetime_binder,
749+
"`for<...>` binders for closures are experimental",
750+
"consider removing `for<...>`"
751+
);
747752
gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental");
748753
gate_all!(generators, "yield syntax is experimental");
749754
gate_all!(raw_ref_op, "raw address of syntax is experimental");

compiler/rustc_ast_pretty/src/pprust/state/expr.rs

+11
Original file line numberDiff line numberDiff line change
@@ -389,13 +389,15 @@ impl<'a> State<'a> {
389389
self.bclose(expr.span, empty);
390390
}
391391
ast::ExprKind::Closure(
392+
ref binder,
392393
capture_clause,
393394
asyncness,
394395
movability,
395396
ref decl,
396397
ref body,
397398
_,
398399
) => {
400+
self.print_closure_binder(binder);
399401
self.print_movability(movability);
400402
self.print_asyncness(asyncness);
401403
self.print_capture_clause(capture_clause);
@@ -594,6 +596,15 @@ impl<'a> State<'a> {
594596
self.end(); // Close enclosing cbox.
595597
}
596598

599+
fn print_closure_binder(&mut self, binder: &ast::ClosureBinder) {
600+
match binder {
601+
ast::ClosureBinder::NotPresent => {}
602+
ast::ClosureBinder::For { generic_params, .. } => {
603+
self.print_formal_generic_params(&generic_params)
604+
}
605+
}
606+
}
607+
597608
fn print_movability(&mut self, movability: ast::Movability) {
598609
match movability {
599610
ast::Movability::Static => self.word_space("static"),

compiler/rustc_borrowck/src/diagnostics/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -891,7 +891,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
891891
let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(local_did);
892892
let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
893893
debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
894-
if let hir::ExprKind::Closure { body, fn_decl_span, .. } = expr {
894+
if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = expr {
895895
for (captured_place, place) in self
896896
.infcx
897897
.tcx
@@ -904,11 +904,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
904904
if target_place == place.as_ref() =>
905905
{
906906
debug!("closure_span: found captured local {:?}", place);
907-
let body = self.infcx.tcx.hir().body(*body);
907+
let body = self.infcx.tcx.hir().body(body);
908908
let generator_kind = body.generator_kind();
909909

910910
return Some((
911-
*fn_decl_span,
911+
fn_decl_span,
912912
generator_kind,
913913
captured_place.get_capture_kind_span(self.infcx.tcx),
914914
captured_place.get_path_span(self.infcx.tcx),

0 commit comments

Comments
 (0)