@@ -31,12 +31,22 @@ use crate::{AllowReturnTypeNotation, FnDeclKind, ImplTraitPosition, TryBlockScop
3131
3232pub ( super ) struct WillCreateDefIdsVisitor ;
3333
34+ /// A `move(...)` expression found while scanning a plain closure body.
3435struct MoveExprOccurrence < ' a > {
36+ /// The `NodeId` of the outer `move(...)` expression.
3537 id : NodeId ,
38+ /// Span of the `move` token, used for the generated binding name.
3639 move_kw_span : Span ,
40+ /// The expression inside `move(...)`; e.g. `foo.bar` in `move(foo.bar)`.
3741 expr : & ' a Expr ,
3842}
3943
44+ /// Collects the `move(...)` expressions that belong to one plain closure body.
45+ ///
46+ /// For `|| move(foo.bar).clone()`, this records the outer `move(foo.bar)`
47+ /// occurrence and the inner expression `foo.bar`. Nested closures, generators,
48+ /// const blocks, and items are lowered as separate bodies, so this visitor does
49+ /// not collect `move(...)` expressions from them.
4050struct MoveExprCollector < ' a > {
4151 occurrences : Vec < MoveExprOccurrence < ' a > > ,
4252}
@@ -53,6 +63,8 @@ impl<'a> Visitor<'a> for MoveExprCollector<'a> {
5363 fn visit_expr ( & mut self , expr : & ' a Expr ) {
5464 match & expr. kind {
5565 ExprKind :: Move ( inner, move_kw_span) => {
66+ // For `move(foo.bar)`, first collect any nested `move(...)`
67+ // expressions in `foo.bar`, then record this outer occurrence.
5668 self . visit_expr ( inner) ;
5769 self . occurrences . push ( MoveExprOccurrence {
5870 id : expr. id ,
@@ -64,6 +76,8 @@ impl<'a> Visitor<'a> for MoveExprCollector<'a> {
6476 _ => walk_expr ( self , expr) ,
6577 }
6678 }
79+
80+ fn visit_item ( & mut self , _: & ' a Item ) { }
6781}
6882
6983impl < ' v > rustc_ast:: visit:: Visitor < ' v > for WillCreateDefIdsVisitor {
@@ -1082,6 +1096,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
10821096 hir:: ExprKind :: Use ( self . lower_expr ( expr) , self . lower_span ( use_kw_span) )
10831097 }
10841098
1099+ // Lowers closure expressions, including the `move(...)` desugaring for
1100+ // plain closures.
10851101 fn lower_expr_closure_expr ( & mut self , e : & Expr , closure : & Closure ) -> hir:: Expr < ' hir > {
10861102 let expr_hir_id = self . lower_node_id ( e. id ) ;
10871103 let attrs = self . lower_attrs ( expr_hir_id, & e. attrs , e. span , Target :: from_expr ( e) ) ;
@@ -1137,8 +1153,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
11371153 fn_arg_span : Span ,
11381154 whole_span : Span ,
11391155 ) -> hir:: Expr < ' hir > {
1156+ // `move(...)` evaluates its inner expression when the closure is created
1157+ // and captures the result by value. For example:
1158+ //
1159+ // `|| move(foo).bar`
1160+ //
1161+ // is lowered roughly as:
1162+ //
1163+ // `let __move_expr_0 = foo; || __move_expr_0.bar`
11401164 let occurrences = MoveExprCollector :: collect ( body) ;
11411165 if occurrences. is_empty ( ) {
1166+ // No `move(...)` expressions in this closure body; lower the closure
1167+ // normally, with no explicit captures.
11421168 return hir:: Expr {
11431169 hir_id : expr_hir_id,
11441170 kind : self . lower_expr_closure (
@@ -1161,6 +1187,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
11611187 let mut bindings = NodeMap :: default ( ) ;
11621188 let mut lowered_occurrences = Vec :: with_capacity ( occurrences. len ( ) ) ;
11631189 for ( index, occurrence) in occurrences. iter ( ) . enumerate ( ) {
1190+ // Create one synthetic local per `move(...)` expression and remember
1191+ // which AST node should be replaced by that local while lowering the
1192+ // closure body.
11641193 let ident =
11651194 Ident :: from_str_and_span ( & format ! ( "__move_expr_{index}" ) , occurrence. move_kw_span ) ;
11661195 let ( pat, binding) = self . pat_ident ( occurrence. expr . span , ident) ;
@@ -1174,6 +1203,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
11741203 self . move_expr_bindings . push ( bindings) ;
11751204 let mut stmts = Vec :: with_capacity ( lowered_occurrences. len ( ) ) ;
11761205 for ( occurrence, pat, _) in & lowered_occurrences {
1206+ // Evaluate the expression inside `move(...)` before creating the
1207+ // closure and store it in a synthetic local:
1208+ // `|| move(foo).bar` becomes roughly
1209+ // `let __move_expr_0 = foo; || __move_expr_0.bar`.
11771210 let init = self . lower_expr ( occurrence. expr ) ;
11781211 stmts. push ( self . stmt_let_pat (
11791212 None ,
@@ -1187,9 +1220,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
11871220 let explicit_captures = self . arena . alloc_from_iter (
11881221 lowered_occurrences
11891222 . iter ( )
1223+ // Force the generated locals to be captured by value even if
1224+ // the lowered closure body only borrows them, as in
1225+ // `move(foo).clone()`.
11901226 . map ( |( _, _, binding) | hir:: ExplicitCapture { var_hir_id : * binding } ) ,
11911227 ) ;
11921228
1229+ // Lower the closure itself while `move_expr_bindings` contains this
1230+ // closure's substitutions, so each `move(...)` in the body is replaced
1231+ // with its generated local.
11931232 let closure_expr = self . arena . alloc ( hir:: Expr {
11941233 hir_id : expr_hir_id,
11951234 kind : self . lower_expr_closure (
@@ -1388,7 +1427,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
13881427 constness : self . lower_constness ( constness) ,
13891428 explicit_captures : & [ ] ,
13901429 } ) ;
1391-
13921430 hir:: ExprKind :: Closure ( c)
13931431 }
13941432
0 commit comments