@@ -28,7 +28,7 @@ use std::collections::BTreeMap;
28
28
use syntax_pos:: Span ;
29
29
30
30
use 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 } ;
32
32
use crate :: dataflow:: Borrows ;
33
33
use crate :: dataflow:: DataflowResultsConsumer ;
34
34
use crate :: dataflow:: FlowAtLocation ;
@@ -1206,25 +1206,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1206
1206
} = self . infcx . tcx . mir_borrowck ( def_id) ;
1207
1207
debug ! ( "{:?} used_mut_upvars={:?}" , def_id, used_mut_upvars) ;
1208
1208
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 ( ) ] ) ;
1228
1210
}
1229
1211
}
1230
1212
AggregateKind :: Adt ( ..)
@@ -1239,6 +1221,80 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1239
1221
}
1240
1222
}
1241
1223
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
+
1242
1298
fn consume_operand (
1243
1299
& mut self ,
1244
1300
context : Context ,
0 commit comments