@@ -30,7 +30,7 @@ use std::rc::Rc;
3030use syntax_pos:: { Span , DUMMY_SP } ;
3131
3232use 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 } ;
3434use crate :: dataflow:: Borrows ;
3535use crate :: dataflow:: DataflowResultsConsumer ;
3636use crate :: dataflow:: FlowAtLocation ;
@@ -1277,25 +1277,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
12771277 } = self . infcx . tcx . mir_borrowck ( def_id) ;
12781278 debug ! ( "{:?} used_mut_upvars={:?}" , def_id, used_mut_upvars) ;
12791279 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 ( ) ] ) ;
12991281 }
13001282 }
13011283 AggregateKind :: Adt ( ..)
@@ -1310,6 +1292,80 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
13101292 }
13111293 }
13121294
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+
13131369 fn consume_operand (
13141370 & mut self ,
13151371 context : Context ,
0 commit comments