Skip to content

Commit 9953aa0

Browse files
committed
lower move(expr) in plain closures
1 parent 9f0f8c8 commit 9953aa0

8 files changed

Lines changed: 236 additions & 43 deletions

File tree

compiler/rustc_ast_lowering/src/expr.rs

Lines changed: 198 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::mem;
22
use std::ops::ControlFlow;
33
use std::sync::Arc;
44

5+
use rustc_ast::node_id::NodeMap;
56
use rustc_ast::*;
67
use rustc_ast_pretty::pprust::expr_to_string;
78
use rustc_data_structures::stack::ensure_sufficient_stack;
@@ -29,6 +30,41 @@ use super::{
2930
use crate::errors::{InvalidLegacyConstGenericArg, UseConstGenericArg, YieldInClosure};
3031
use crate::{AllowReturnTypeNotation, FnDeclKind, ImplTraitPosition, TryBlockScope};
3132

33+
struct MoveExprOccurrence<'a> {
34+
id: NodeId,
35+
move_kw_span: Span,
36+
expr: &'a Expr,
37+
}
38+
39+
struct MoveExprCollector<'a> {
40+
occurrences: Vec<MoveExprOccurrence<'a>>,
41+
}
42+
43+
impl<'a> MoveExprCollector<'a> {
44+
fn collect(expr: &'a Expr) -> Vec<MoveExprOccurrence<'a>> {
45+
let mut this = Self { occurrences: Vec::new() };
46+
this.visit_expr(expr);
47+
this.occurrences
48+
}
49+
}
50+
51+
impl<'a> Visitor<'a> for MoveExprCollector<'a> {
52+
fn visit_expr(&mut self, expr: &'a Expr) {
53+
match &expr.kind {
54+
ExprKind::Move(inner, move_kw_span) => {
55+
self.visit_expr(inner);
56+
self.occurrences.push(MoveExprOccurrence {
57+
id: expr.id,
58+
move_kw_span: *move_kw_span,
59+
expr: inner,
60+
});
61+
}
62+
ExprKind::Closure(..) | ExprKind::Gen(..) | ExprKind::ConstBlock(..) => {}
63+
_ => walk_expr(self, expr),
64+
}
65+
}
66+
}
67+
3268
struct WillCreateDefIdsVisitor {}
3369

3470
impl<'v> rustc_ast::visit::Visitor<'v> for WillCreateDefIdsVisitor {
@@ -95,11 +131,12 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
95131
ExprKind::ForLoop { pat, iter, body, label, kind } => {
96132
return self.lower_expr_for(e, pat, iter, body, *label, *kind);
97133
}
134+
ExprKind::Closure(box closure) => return self.lower_expr_closure_expr(e, closure),
98135
_ => (),
99136
}
100137

101138
let expr_hir_id = self.lower_node_id(e.id);
102-
let attrs = self.lower_attrs(expr_hir_id, &e.attrs, e.span, Target::from_expr(e));
139+
self.lower_attrs(expr_hir_id, &e.attrs, e.span, Target::from_expr(e));
103140

104141
let kind = match &e.kind {
105142
ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)),
@@ -219,48 +256,34 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
219256
self.dcx().span_delayed_bug(*move_kw_span, "invalid move(expr)"),
220257
);
221258
}
222-
self.dcx().emit_err(MoveExprOnlyInPlainClosures { span: *move_kw_span });
223-
hir::ExprKind::Err(
224-
self.dcx().span_delayed_bug(*move_kw_span, "invalid move(expr)"),
225-
)
259+
if let Some((ident, binding)) = self
260+
.move_expr_bindings
261+
.last()
262+
.and_then(|bindings| bindings.get(&e.id).copied())
263+
{
264+
hir::ExprKind::Path(hir::QPath::Resolved(
265+
None,
266+
self.arena.alloc(hir::Path {
267+
span: self.lower_span(e.span),
268+
res: Res::Local(binding),
269+
segments: arena_vec![
270+
self;
271+
hir::PathSegment::new(
272+
self.lower_ident(ident),
273+
self.next_id(),
274+
Res::Local(binding),
275+
)
276+
],
277+
}),
278+
))
279+
} else {
280+
self.dcx().emit_err(MoveExprOnlyInPlainClosures { span: *move_kw_span });
281+
hir::ExprKind::Err(
282+
self.dcx().span_delayed_bug(*move_kw_span, "invalid move(expr)"),
283+
)
284+
}
226285
}
227286
ExprKind::Use(expr, use_kw_span) => self.lower_expr_use(*use_kw_span, expr),
228-
ExprKind::Closure(box Closure {
229-
binder,
230-
capture_clause,
231-
constness,
232-
coroutine_kind,
233-
movability,
234-
fn_decl,
235-
body,
236-
fn_decl_span,
237-
fn_arg_span,
238-
}) => match coroutine_kind {
239-
Some(coroutine_kind) => self.lower_expr_coroutine_closure(
240-
binder,
241-
*capture_clause,
242-
e.id,
243-
expr_hir_id,
244-
*coroutine_kind,
245-
*constness,
246-
fn_decl,
247-
body,
248-
*fn_decl_span,
249-
*fn_arg_span,
250-
),
251-
None => self.lower_expr_closure(
252-
attrs,
253-
binder,
254-
*capture_clause,
255-
e.id,
256-
*constness,
257-
*movability,
258-
fn_decl,
259-
body,
260-
*fn_decl_span,
261-
*fn_arg_span,
262-
),
263-
},
264287
ExprKind::Gen(capture_clause, block, genblock_kind, decl_span) => {
265288
let desugaring_kind = match genblock_kind {
266289
GenBlockKind::Async => hir::CoroutineDesugaring::Async,
@@ -395,7 +418,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
395418

396419
ExprKind::Try(sub_expr) => self.lower_expr_try(e.span, sub_expr),
397420

398-
ExprKind::Paren(_) | ExprKind::ForLoop { .. } => {
421+
ExprKind::Paren(_) | ExprKind::ForLoop { .. } | ExprKind::Closure(..) => {
399422
unreachable!("already handled")
400423
}
401424

@@ -804,6 +827,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
804827
fn_arg_span: None,
805828
kind: hir::ClosureKind::Coroutine(coroutine_kind),
806829
constness: hir::Constness::NotConst,
830+
explicit_captures: &[],
807831
}))
808832
}
809833

@@ -1067,6 +1091,134 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
10671091
hir::ExprKind::Use(self.lower_expr(expr), self.lower_span(use_kw_span))
10681092
}
10691093

1094+
fn lower_expr_closure_expr(&mut self, e: &Expr, closure: &Closure) -> hir::Expr<'hir> {
1095+
let expr_hir_id = self.lower_node_id(e.id);
1096+
let attrs = self.lower_attrs(expr_hir_id, &e.attrs, e.span, Target::from_expr(e));
1097+
1098+
match closure.coroutine_kind {
1099+
Some(coroutine_kind) => hir::Expr {
1100+
hir_id: expr_hir_id,
1101+
kind: self.lower_expr_coroutine_closure(
1102+
&closure.binder,
1103+
closure.capture_clause,
1104+
e.id,
1105+
expr_hir_id,
1106+
coroutine_kind,
1107+
closure.constness,
1108+
&closure.fn_decl,
1109+
&closure.body,
1110+
closure.fn_decl_span,
1111+
closure.fn_arg_span,
1112+
),
1113+
span: self.lower_span(e.span),
1114+
},
1115+
None => self.lower_expr_plain_closure_with_move_exprs(
1116+
expr_hir_id,
1117+
attrs,
1118+
&closure.binder,
1119+
closure.capture_clause,
1120+
e.id,
1121+
closure.constness,
1122+
closure.movability,
1123+
&closure.fn_decl,
1124+
&closure.body,
1125+
closure.fn_decl_span,
1126+
closure.fn_arg_span,
1127+
e.span,
1128+
),
1129+
}
1130+
}
1131+
1132+
fn lower_expr_plain_closure_with_move_exprs(
1133+
&mut self,
1134+
expr_hir_id: HirId,
1135+
attrs: &[rustc_hir::Attribute],
1136+
binder: &ClosureBinder,
1137+
capture_clause: CaptureBy,
1138+
closure_id: NodeId,
1139+
constness: Const,
1140+
movability: Movability,
1141+
decl: &FnDecl,
1142+
body: &Expr,
1143+
fn_decl_span: Span,
1144+
fn_arg_span: Span,
1145+
whole_span: Span,
1146+
) -> hir::Expr<'hir> {
1147+
let occurrences = MoveExprCollector::collect(body);
1148+
if occurrences.is_empty() {
1149+
return hir::Expr {
1150+
hir_id: expr_hir_id,
1151+
kind: self.lower_expr_closure(
1152+
attrs,
1153+
binder,
1154+
capture_clause,
1155+
closure_id,
1156+
constness,
1157+
movability,
1158+
decl,
1159+
body,
1160+
fn_decl_span,
1161+
fn_arg_span,
1162+
&[],
1163+
),
1164+
span: self.lower_span(whole_span),
1165+
};
1166+
}
1167+
1168+
let mut bindings = NodeMap::default();
1169+
let mut lowered_occurrences = Vec::with_capacity(occurrences.len());
1170+
for (index, occurrence) in occurrences.iter().enumerate() {
1171+
let ident =
1172+
Ident::from_str_and_span(&format!("__move_expr_{index}"), occurrence.move_kw_span);
1173+
let (pat, binding) = self.pat_ident(occurrence.expr.span, ident);
1174+
bindings.insert(occurrence.id, (ident, binding));
1175+
lowered_occurrences.push((occurrence, pat, binding));
1176+
}
1177+
1178+
self.move_expr_bindings.push(bindings);
1179+
let mut stmts = Vec::with_capacity(lowered_occurrences.len());
1180+
for (occurrence, pat, _) in &lowered_occurrences {
1181+
let init = self.lower_expr(occurrence.expr);
1182+
stmts.push(self.stmt_let_pat(
1183+
None,
1184+
occurrence.expr.span,
1185+
Some(init),
1186+
*pat,
1187+
hir::LocalSource::Normal,
1188+
));
1189+
}
1190+
1191+
let explicit_captures = self.arena.alloc_from_iter(lowered_occurrences.iter().map(
1192+
|(occurrence, _, binding)| hir::ExplicitCapture {
1193+
var_hir_id: *binding,
1194+
origin_span: self.lower_span(occurrence.move_kw_span),
1195+
},
1196+
));
1197+
1198+
let closure_expr = self.arena.alloc(hir::Expr {
1199+
hir_id: expr_hir_id,
1200+
kind: self.lower_expr_closure(
1201+
attrs,
1202+
binder,
1203+
capture_clause,
1204+
closure_id,
1205+
constness,
1206+
movability,
1207+
decl,
1208+
body,
1209+
fn_decl_span,
1210+
fn_arg_span,
1211+
explicit_captures,
1212+
),
1213+
span: self.lower_span(whole_span),
1214+
});
1215+
self.move_expr_bindings.pop();
1216+
1217+
let stmts = self.arena.alloc_from_iter(stmts);
1218+
let block = self.block_all(whole_span, stmts, Some(closure_expr));
1219+
self.expr(whole_span, hir::ExprKind::Block(block, None))
1220+
}
1221+
10701222
fn lower_expr_closure(
10711223
&mut self,
10721224
attrs: &[rustc_hir::Attribute],
@@ -1079,6 +1231,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
10791231
body: &Expr,
10801232
fn_decl_span: Span,
10811233
fn_arg_span: Span,
1234+
explicit_captures: &'hir [hir::ExplicitCapture],
10821235
) -> hir::ExprKind<'hir> {
10831236
let closure_def_id = self.local_def_id(closure_id);
10841237
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
@@ -1120,6 +1273,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
11201273
fn_arg_span: Some(self.lower_span(fn_arg_span)),
11211274
kind: closure_kind,
11221275
constness: self.lower_constness(constness),
1276+
explicit_captures,
11231277
});
11241278

11251279
hir::ExprKind::Closure(c)
@@ -1241,7 +1395,8 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
12411395
// knows that a `FnDecl` output type like `-> &str` actually means
12421396
// "coroutine that returns &str", rather than directly returning a `&str`.
12431397
kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring),
1244-
constness: self.lower_constness(constness),
1398+
constness: hir::Constness::NotConst,
1399+
explicit_captures: &[],
12451400
});
12461401
hir::ExprKind::Closure(c)
12471402
}

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ struct LoweringContext<'a, 'hir, R> {
148148
allow_async_fn_traits: Arc<[Symbol]>,
149149

150150
delayed_lints: Vec<DelayedLint>,
151+
move_expr_bindings: Vec<NodeMap<(Ident, HirId)>>,
151152

152153
attribute_parser: AttributeParser<'hir>,
153154
}
@@ -205,6 +206,7 @@ impl<'a, 'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'a, 'hir, R> {
205206
// interact with `gen`/`async gen` blocks
206207
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
207208

209+
move_expr_bindings: Vec::new(),
208210
attribute_parser: AttributeParser::new(
209211
tcx.sess,
210212
tcx.features(),

compiler/rustc_hir/src/hir.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1690,6 +1690,13 @@ pub struct Closure<'hir> {
16901690
/// The span of the argument block `|...|`
16911691
pub fn_arg_span: Option<Span>,
16921692
pub kind: ClosureKind,
1693+
pub explicit_captures: &'hir [ExplicitCapture],
1694+
}
1695+
1696+
#[derive(Debug, Clone, Copy, HashStable_Generic)]
1697+
pub struct ExplicitCapture {
1698+
pub var_hir_id: HirId,
1699+
pub origin_span: Span,
16931700
}
16941701

16951702
#[derive(Clone, PartialEq, Eq, Debug, Copy, Hash, HashStable_Generic, Encodable, Decodable)]

compiler/rustc_hir/src/intravisit.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
894894
fn_arg_span: _,
895895
kind: _,
896896
constness: _,
897+
explicit_captures: _,
897898
}) => {
898899
walk_list!(visitor, visit_generic_param, bound_generic_params);
899900
try_visit!(visitor.visit_fn(FnKind::Closure, fn_decl, body, *span, def_id));

compiler/rustc_hir_pretty/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,6 +1643,7 @@ impl<'a> State<'a> {
16431643
fn_arg_span: _,
16441644
kind: _,
16451645
def_id: _,
1646+
explicit_captures: _,
16461647
}) => {
16471648
self.print_closure_binder(binder, bound_generic_params);
16481649
self.print_constness(constness);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#![allow(incomplete_features)]
2+
#![feature(move_expr)]
3+
4+
fn main() {
5+
let _ = move(String::from("nope"));
6+
//~^ ERROR `move(expr)` is only supported in plain closures
7+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: `move(expr)` is only supported in plain closures
2+
--> $DIR/outside-plain-closure.rs:5:13
3+
|
4+
LL | let _ = move(String::from("nope"));
5+
| ^^^^
6+
7+
error: aborting due to 1 previous error
8+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//@ check-pass
2+
#![allow(incomplete_features)]
3+
#![feature(move_expr)]
4+
5+
fn main() {
6+
let s = String::from("hello");
7+
let c = || {
8+
let t = move(s);
9+
println!("{}", t.len());
10+
};
11+
c();
12+
}

0 commit comments

Comments
 (0)