@@ -30,7 +30,7 @@ use std::rc::Rc;
30
30
use syntax_pos:: { Span , DUMMY_SP } ;
31
31
32
32
use crate :: dataflow:: indexes:: { BorrowIndex , InitIndex , MoveOutIndex , MovePathIndex } ;
33
- use crate :: dataflow:: move_paths:: { HasMoveData , LookupResult , MoveData , MoveError } ;
33
+ use crate :: dataflow:: move_paths:: { HasMoveData , InitLocation , LookupResult , MoveData , MoveError } ;
34
34
use crate :: dataflow:: Borrows ;
35
35
use crate :: dataflow:: DataflowResultsConsumer ;
36
36
use crate :: dataflow:: FlowAtLocation ;
@@ -1277,25 +1277,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1277
1277
} = self . infcx . tcx . mir_borrowck ( def_id) ;
1278
1278
debug ! ( "{:?} used_mut_upvars={:?}" , def_id, used_mut_upvars) ;
1279
1279
for field in used_mut_upvars {
1280
- // This relies on the current way that by-value
1281
- // captures of a closure are copied/moved directly
1282
- // when generating MIR.
1283
- match operands[ field. index ( ) ] {
1284
- Operand :: Move ( Place :: Base ( PlaceBase :: Local ( local) ) )
1285
- | Operand :: Copy ( Place :: Base ( PlaceBase :: Local ( local) ) ) => {
1286
- self . used_mut . insert ( local) ;
1287
- }
1288
- Operand :: Move ( ref place @ Place :: Projection ( _) )
1289
- | Operand :: Copy ( ref place @ Place :: Projection ( _) ) => {
1290
- if let Some ( field) = place. is_upvar_field_projection (
1291
- self . mir , & self . infcx . tcx ) {
1292
- self . used_mut_upvars . push ( field) ;
1293
- }
1294
- }
1295
- Operand :: Move ( Place :: Base ( PlaceBase :: Static ( ..) ) )
1296
- | Operand :: Copy ( Place :: Base ( PlaceBase :: Static ( ..) ) )
1297
- | Operand :: Constant ( ..) => { }
1298
- }
1280
+ self . propagate_closure_used_mut_upvar ( & operands[ field. index ( ) ] ) ;
1299
1281
}
1300
1282
}
1301
1283
AggregateKind :: Adt ( ..)
@@ -1310,6 +1292,80 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1310
1292
}
1311
1293
}
1312
1294
1295
+ fn propagate_closure_used_mut_upvar ( & mut self , operand : & Operand < ' tcx > ) {
1296
+ let propagate_closure_used_mut_place = |this : & mut Self , place : & Place < ' tcx > | {
1297
+ match * place {
1298
+ Place :: Projection { .. } => {
1299
+ if let Some ( field) = place. is_upvar_field_projection (
1300
+ this. mir , & this. infcx . tcx ) {
1301
+ this. used_mut_upvars . push ( field) ;
1302
+ }
1303
+ }
1304
+ Place :: Base ( PlaceBase :: Local ( local) ) => {
1305
+ this. used_mut . insert ( local) ;
1306
+ }
1307
+ Place :: Base ( PlaceBase :: Static ( _) ) => { }
1308
+ }
1309
+ } ;
1310
+
1311
+ // This relies on the current way that by-value
1312
+ // captures of a closure are copied/moved directly
1313
+ // when generating MIR.
1314
+ match * operand {
1315
+ Operand :: Move ( Place :: Base ( PlaceBase :: Local ( local) ) )
1316
+ | Operand :: Copy ( Place :: Base ( PlaceBase :: Local ( local) ) )
1317
+ if self . mir . local_decls [ local] . is_user_variable . is_none ( ) =>
1318
+ {
1319
+ if self . mir . local_decls [ local] . ty . is_mutable_pointer ( ) {
1320
+ // The variable will be marked as mutable by the borrow.
1321
+ return ;
1322
+ }
1323
+ // This is an edge case where we have a `move` closure
1324
+ // inside a non-move closure, and the inner closure
1325
+ // contains a mutation:
1326
+ //
1327
+ // let mut i = 0;
1328
+ // || { move || { i += 1; }; };
1329
+ //
1330
+ // In this case our usual strategy of assuming that the
1331
+ // variable will be captured by mutable reference is
1332
+ // wrong, since `i` can be copied into the inner
1333
+ // closure from a shared reference.
1334
+ //
1335
+ // As such we have to search for the local that this
1336
+ // capture comes from and mark it as being used as mut.
1337
+
1338
+ let temp_mpi = self . move_data . rev_lookup . find_local ( local) ;
1339
+ let init = if let [ init_index] = * self . move_data . init_path_map [ temp_mpi] {
1340
+ & self . move_data . inits [ init_index]
1341
+ } else {
1342
+ bug ! ( "temporary should be initialized exactly once" )
1343
+ } ;
1344
+
1345
+ let loc = match init. location {
1346
+ InitLocation :: Statement ( stmt) => stmt,
1347
+ _ => bug ! ( "temporary initialized in arguments" ) ,
1348
+ } ;
1349
+
1350
+ let bbd = & self . mir [ loc. block ] ;
1351
+ let stmt = & bbd. statements [ loc. statement_index ] ;
1352
+ debug ! ( "temporary assigned in: stmt={:?}" , stmt) ;
1353
+
1354
+ if let StatementKind :: Assign ( _, box Rvalue :: Ref ( _, _, ref source) ) = stmt. kind {
1355
+ propagate_closure_used_mut_place ( self , source) ;
1356
+ } else {
1357
+ bug ! ( "closures should only capture user variables \
1358
+ or references to user variables") ;
1359
+ }
1360
+ }
1361
+ Operand :: Move ( ref place)
1362
+ | Operand :: Copy ( ref place) => {
1363
+ propagate_closure_used_mut_place ( self , place) ;
1364
+ }
1365
+ Operand :: Constant ( ..) => { }
1366
+ }
1367
+ }
1368
+
1313
1369
fn consume_operand (
1314
1370
& mut self ,
1315
1371
context : Context ,
0 commit comments