@@ -28,7 +28,7 @@ use std::collections::BTreeMap;
2828use syntax_pos:: Span ;
2929
3030use crate :: dataflow:: indexes:: { BorrowIndex , InitIndex , MoveOutIndex , MovePathIndex } ;
31- use crate :: dataflow:: move_paths:: { HasMoveData , LookupResult , MoveData , MoveError } ;
31+ use crate :: dataflow:: move_paths:: { HasMoveData , InitLocation , LookupResult , MoveData , MoveError } ;
3232use crate :: dataflow:: Borrows ;
3333use crate :: dataflow:: DataflowResultsConsumer ;
3434use crate :: dataflow:: FlowAtLocation ;
@@ -1206,25 +1206,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
12061206 } = self . infcx . tcx . mir_borrowck ( def_id) ;
12071207 debug ! ( "{:?} used_mut_upvars={:?}" , def_id, used_mut_upvars) ;
12081208 for field in used_mut_upvars {
1209- // This relies on the current way that by-value
1210- // captures of a closure are copied/moved directly
1211- // when generating MIR.
1212- match operands[ field. index ( ) ] {
1213- Operand :: Move ( Place :: Base ( PlaceBase :: Local ( local) ) )
1214- | Operand :: Copy ( Place :: Base ( PlaceBase :: Local ( local) ) ) => {
1215- self . used_mut . insert ( local) ;
1216- }
1217- Operand :: Move ( ref place @ Place :: Projection ( _) )
1218- | Operand :: Copy ( ref place @ Place :: Projection ( _) ) => {
1219- if let Some ( field) = place. is_upvar_field_projection (
1220- self . mir , & self . infcx . tcx ) {
1221- self . used_mut_upvars . push ( field) ;
1222- }
1223- }
1224- Operand :: Move ( Place :: Base ( PlaceBase :: Static ( ..) ) )
1225- | Operand :: Copy ( Place :: Base ( PlaceBase :: Static ( ..) ) )
1226- | Operand :: Constant ( ..) => { }
1227- }
1209+ self . propagate_closure_used_mut_upvar ( & operands[ field. index ( ) ] ) ;
12281210 }
12291211 }
12301212 AggregateKind :: Adt ( ..)
@@ -1239,6 +1221,80 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
12391221 }
12401222 }
12411223
1224+ fn propagate_closure_used_mut_upvar ( & mut self , operand : & Operand < ' tcx > ) {
1225+ let propagate_closure_used_mut_place = |this : & mut Self , place : & Place < ' tcx > | {
1226+ match * place {
1227+ Place :: Projection { .. } => {
1228+ if let Some ( field) = place. is_upvar_field_projection (
1229+ this. mir , & this. infcx . tcx ) {
1230+ this. used_mut_upvars . push ( field) ;
1231+ }
1232+ }
1233+ Place :: Base ( PlaceBase :: Local ( local) ) => {
1234+ this. used_mut . insert ( local) ;
1235+ }
1236+ Place :: Base ( PlaceBase :: Static ( _) ) => { }
1237+ }
1238+ } ;
1239+
1240+ // This relies on the current way that by-value
1241+ // captures of a closure are copied/moved directly
1242+ // when generating MIR.
1243+ match * operand {
1244+ Operand :: Move ( Place :: Base ( PlaceBase :: Local ( local) ) )
1245+ | Operand :: Copy ( Place :: Base ( PlaceBase :: Local ( local) ) )
1246+ if self . mir . local_decls [ local] . is_user_variable . is_none ( ) =>
1247+ {
1248+ if self . mir . local_decls [ local] . ty . is_mutable_pointer ( ) {
1249+ // The variable will be marked as mutable by the borrow.
1250+ return ;
1251+ }
1252+ // This is an edge case where we have a `move` closure
1253+ // inside a non-move closure, and the inner closure
1254+ // contains a mutation:
1255+ //
1256+ // let mut i = 0;
1257+ // || { move || { i += 1; }; };
1258+ //
1259+ // In this case our usual strategy of assuming that the
1260+ // variable will be captured by mutable reference is
1261+ // wrong, since `i` can be copied into the inner
1262+ // closure from a shared reference.
1263+ //
1264+ // As such we have to search for the local that this
1265+ // capture comes from and mark it as being used as mut.
1266+
1267+ let temp_mpi = self . move_data . rev_lookup . find_local ( local) ;
1268+ let init = if let [ init_index] = * self . move_data . init_path_map [ temp_mpi] {
1269+ & self . move_data . inits [ init_index]
1270+ } else {
1271+ bug ! ( "temporary should be initialized exactly once" )
1272+ } ;
1273+
1274+ let loc = match init. location {
1275+ InitLocation :: Statement ( stmt) => stmt,
1276+ _ => bug ! ( "temporary initialized in arguments" ) ,
1277+ } ;
1278+
1279+ let bbd = & self . mir [ loc. block ] ;
1280+ let stmt = & bbd. statements [ loc. statement_index ] ;
1281+ debug ! ( "temporary assigned in: stmt={:?}" , stmt) ;
1282+
1283+ if let StatementKind :: Assign ( _, box Rvalue :: Ref ( _, _, ref source) ) = stmt. kind {
1284+ propagate_closure_used_mut_place ( self , source) ;
1285+ } else {
1286+ bug ! ( "closures should only capture user variables \
1287+ or references to user variables") ;
1288+ }
1289+ }
1290+ Operand :: Move ( ref place)
1291+ | Operand :: Copy ( ref place) => {
1292+ propagate_closure_used_mut_place ( self , place) ;
1293+ }
1294+ Operand :: Constant ( ..) => { }
1295+ }
1296+ }
1297+
12421298 fn consume_operand (
12431299 & mut self ,
12441300 context : Context ,
0 commit comments