Skip to content

Commit 1a19a26

Browse files
committed
implement move(expr)
1 parent 3d1b572 commit 1a19a26

21 files changed

Lines changed: 317 additions & 41 deletions

File tree

compiler/rustc_ast/src/ast.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1593,6 +1593,7 @@ impl Expr {
15931593
// need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b`
15941594
// but we need to print `(let _ = a) < b` as-is with parens.
15951595
| ExprKind::Let(..)
1596+
| ExprKind::Move(..)
15961597
| ExprKind::Unary(..) => ExprPrecedence::Prefix,
15971598

15981599
// Need parens if and only if there are prefix attributes.
@@ -1762,6 +1763,8 @@ pub enum ExprKind {
17621763
Binary(BinOp, Box<Expr>, Box<Expr>),
17631764
/// A unary operation (e.g., `!x`, `*x`).
17641765
Unary(UnOp, Box<Expr>),
1766+
/// A `move(expr)` expression.
1767+
Move(Box<Expr>, Span),
17651768
/// A literal (e.g., `1`, `"foo"`).
17661769
Lit(token::Lit),
17671770
/// A cast (e.g., `foo as f64`).

compiler/rustc_ast/src/util/classify.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool {
108108
Assign(e, _, _)
109109
| AssignOp(_, e, _)
110110
| Await(e, _)
111+
| Move(e, _)
111112
| Use(e, _)
112113
| Binary(_, e, _)
113114
| Call(e, _)
@@ -183,6 +184,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
183184
| Ret(Some(e))
184185
| Unary(_, e)
185186
| Yeet(Some(e))
187+
| Move(e, _)
186188
| Become(e) => {
187189
expr = e;
188190
}

compiler/rustc_ast/src/visit.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1023,7 +1023,9 @@ macro_rules! common_visitor_and_walkers {
10231023
visit_visitable!($($mut)? vis, block, opt_label),
10241024
ExprKind::Gen(capt, body, kind, decl_span) =>
10251025
visit_visitable!($($mut)? vis, capt, body, kind, decl_span),
1026-
ExprKind::Await(expr, span) | ExprKind::Use(expr, span) =>
1026+
ExprKind::Await(expr, span)
1027+
| ExprKind::Move(expr, span)
1028+
| ExprKind::Use(expr, span) =>
10271029
visit_visitable!($($mut)? vis, expr, span),
10281030
ExprKind::Assign(lhs, rhs, span) =>
10291031
visit_visitable!($($mut)? vis, lhs, rhs, span),

compiler/rustc_ast_lowering/src/errors.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,13 @@ pub(crate) struct ClosureCannotBeStatic {
136136
pub fn_decl_span: Span,
137137
}
138138

139+
#[derive(Diagnostic)]
140+
#[diag("`move(expr)` is only supported in plain closures")]
141+
pub(crate) struct MoveExprOnlyInPlainClosures {
142+
#[primary_span]
143+
pub span: Span,
144+
}
145+
139146
#[derive(Diagnostic)]
140147
#[diag("functional record updates are not allowed in destructuring assignments")]
141148
pub(crate) struct FunctionalRecordUpdateDestructuringAssignment {

compiler/rustc_ast_lowering/src/expr.rs

Lines changed: 200 additions & 39 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;
@@ -21,15 +22,50 @@ use visit::{Visitor, walk_expr};
2122
use super::errors::{
2223
AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, ClosureCannotBeStatic,
2324
CoroutineTooManyParameters, FunctionalRecordUpdateDestructuringAssignment,
24-
InclusiveRangeWithNoEnd, MatchArmWithNoBody, NeverPatternWithBody, NeverPatternWithGuard,
25-
UnderscoreExprLhsAssign,
25+
InclusiveRangeWithNoEnd, MatchArmWithNoBody, MoveExprOnlyInPlainClosures,
26+
NeverPatternWithBody, NeverPatternWithGuard, UnderscoreExprLhsAssign,
2627
};
2728
use super::{
2829
GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt,
2930
};
3031
use crate::errors::{InvalidLegacyConstGenericArg, UseConstGenericArg, YieldInClosure};
3132
use crate::{AllowReturnTypeNotation, FnDeclKind, ImplTraitPosition, TryBlockScope};
3233

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

3571
impl<'v> rustc_ast::visit::Visitor<'v> for WillCreateDefIdsVisitor {
@@ -96,11 +132,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
96132
ExprKind::ForLoop { pat, iter, body, label, kind } => {
97133
return self.lower_expr_for(e, pat, iter, body, *label, *kind);
98134
}
135+
ExprKind::Closure(box closure) => return self.lower_expr_closure_expr(e, closure),
99136
_ => (),
100137
}
101138

102139
let expr_hir_id = self.lower_node_id(e.id);
103-
let attrs = self.lower_attrs(expr_hir_id, &e.attrs, e.span, Target::from_expr(e));
140+
self.lower_attrs(expr_hir_id, &e.attrs, e.span, Target::from_expr(e));
104141

105142
let kind = match &e.kind {
106143
ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)),
@@ -213,42 +250,35 @@ impl<'hir> LoweringContext<'_, 'hir> {
213250
},
214251
),
215252
ExprKind::Await(expr, await_kw_span) => self.lower_expr_await(*await_kw_span, expr),
253+
ExprKind::Move(_, move_kw_span) => {
254+
if let Some((ident, binding)) = self
255+
.move_expr_bindings
256+
.last()
257+
.and_then(|bindings| bindings.get(&e.id).copied())
258+
{
259+
hir::ExprKind::Path(hir::QPath::Resolved(
260+
None,
261+
self.arena.alloc(hir::Path {
262+
span: self.lower_span(e.span),
263+
res: Res::Local(binding),
264+
segments: arena_vec![
265+
self;
266+
hir::PathSegment::new(
267+
self.lower_ident(ident),
268+
self.next_id(),
269+
Res::Local(binding),
270+
)
271+
],
272+
}),
273+
))
274+
} else {
275+
self.dcx().emit_err(MoveExprOnlyInPlainClosures { span: *move_kw_span });
276+
hir::ExprKind::Err(
277+
self.dcx().span_delayed_bug(*move_kw_span, "invalid move(expr)"),
278+
)
279+
}
280+
}
216281
ExprKind::Use(expr, use_kw_span) => self.lower_expr_use(*use_kw_span, expr),
217-
ExprKind::Closure(box Closure {
218-
binder,
219-
capture_clause,
220-
constness,
221-
coroutine_kind,
222-
movability,
223-
fn_decl,
224-
body,
225-
fn_decl_span,
226-
fn_arg_span,
227-
}) => match coroutine_kind {
228-
Some(coroutine_kind) => self.lower_expr_coroutine_closure(
229-
binder,
230-
*capture_clause,
231-
e.id,
232-
expr_hir_id,
233-
*coroutine_kind,
234-
fn_decl,
235-
body,
236-
*fn_decl_span,
237-
*fn_arg_span,
238-
),
239-
None => self.lower_expr_closure(
240-
attrs,
241-
binder,
242-
*capture_clause,
243-
e.id,
244-
*constness,
245-
*movability,
246-
fn_decl,
247-
body,
248-
*fn_decl_span,
249-
*fn_arg_span,
250-
),
251-
},
252282
ExprKind::Gen(capture_clause, block, genblock_kind, decl_span) => {
253283
let desugaring_kind = match genblock_kind {
254284
GenBlockKind::Async => hir::CoroutineDesugaring::Async,
@@ -383,7 +413,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
383413

384414
ExprKind::Try(sub_expr) => self.lower_expr_try(e.span, sub_expr),
385415

386-
ExprKind::Paren(_) | ExprKind::ForLoop { .. } => {
416+
ExprKind::Paren(_) | ExprKind::ForLoop { .. } | ExprKind::Closure(..) => {
387417
unreachable!("already handled")
388418
}
389419

@@ -792,6 +822,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
792822
fn_arg_span: None,
793823
kind: hir::ClosureKind::Coroutine(coroutine_kind),
794824
constness: hir::Constness::NotConst,
825+
explicit_captures: &[],
795826
}))
796827
}
797828

@@ -1055,6 +1086,133 @@ impl<'hir> LoweringContext<'_, 'hir> {
10551086
hir::ExprKind::Use(self.lower_expr(expr), self.lower_span(use_kw_span))
10561087
}
10571088

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

11061266
hir::ExprKind::Closure(c)
@@ -1218,6 +1378,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
12181378
// "coroutine that returns &str", rather than directly returning a `&str`.
12191379
kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring),
12201380
constness: hir::Constness::NotConst,
1381+
explicit_captures: &[],
12211382
});
12221383
hir::ExprKind::Closure(c)
12231384
}

compiler/rustc_ast_lowering/src/lib.rs

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

155155
delayed_lints: Vec<DelayedLint>,
156+
move_expr_bindings: Vec<NodeMap<(Ident, HirId)>>,
156157

157158
attribute_parser: AttributeParser<'hir>,
158159
}
@@ -215,6 +216,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
215216
// interact with `gen`/`async gen` blocks
216217
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
217218

219+
move_expr_bindings: Vec::new(),
218220
attribute_parser: AttributeParser::new(
219221
tcx.sess,
220222
tcx.features(),

0 commit comments

Comments
 (0)