@@ -180,7 +180,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
180
180
debug ! ( "seed place {:?}" , place) ;
181
181
182
182
let upvar_id = ty:: UpvarId :: new ( * var_hir_id, local_def_id) ;
183
- let capture_kind = self . init_capture_kind ( capture_clause, upvar_id, span) ;
183
+ let capture_kind =
184
+ self . init_capture_kind_for_place ( & place, capture_clause, upvar_id, span) ;
184
185
let fake_info = ty:: CaptureInfo {
185
186
capture_kind_expr_id : None ,
186
187
path_expr_id : None ,
@@ -449,7 +450,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
449
450
base => bug ! ( "Expected upvar, found={:?}" , base) ,
450
451
} ;
451
452
452
- let place = restrict_capture_precision ( place, capture_info . capture_kind ) ;
453
+ let place = restrict_capture_precision ( place) ;
453
454
454
455
let min_cap_list = match root_var_min_capture_list. get_mut ( & var_hir_id) {
455
456
None => {
@@ -897,15 +898,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
897
898
}
898
899
}
899
900
900
- fn init_capture_kind (
901
+ fn init_capture_kind_for_place (
901
902
& self ,
903
+ place : & Place < ' tcx > ,
902
904
capture_clause : hir:: CaptureBy ,
903
905
upvar_id : ty:: UpvarId ,
904
906
closure_span : Span ,
905
907
) -> ty:: UpvarCapture < ' tcx > {
906
908
match capture_clause {
907
- hir:: CaptureBy :: Value => ty:: UpvarCapture :: ByValue ( None ) ,
908
- hir:: CaptureBy :: Ref => {
909
+ // In case of a move closure if the data is accessed through a reference we
910
+ // want to capture by ref to allow precise capture using reborrows.
911
+ //
912
+ // If the data will be moved out of this place, then the place will be truncated
913
+ // at the first Deref in `adjust_upvar_borrow_kind_for_consume` and then moved into
914
+ // the closure.
915
+ hir:: CaptureBy :: Value if !place. deref_tys ( ) . any ( ty:: TyS :: is_ref) => {
916
+ ty:: UpvarCapture :: ByValue ( None )
917
+ }
918
+ hir:: CaptureBy :: Value | hir:: CaptureBy :: Ref => {
909
919
let origin = UpvarRegion ( upvar_id, closure_span) ;
910
920
let upvar_region = self . next_region_var ( origin) ;
911
921
let upvar_borrow = ty:: UpvarBorrow { kind : ty:: ImmBorrow , region : upvar_region } ;
@@ -1109,12 +1119,18 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
1109
1119
place_with_id, diag_expr_id, mode
1110
1120
) ;
1111
1121
1112
- // we only care about moves
1113
- match mode {
1114
- euv:: Copy => {
1115
- return ;
1116
- }
1117
- euv:: Move => { }
1122
+ let place = truncate_capture_for_move ( place_with_id. place . clone ( ) ) ;
1123
+ match ( self . capture_clause , mode) {
1124
+ // In non-move closures, we only care about moves
1125
+ ( hir:: CaptureBy :: Ref , euv:: Copy ) => return ,
1126
+
1127
+ ( hir:: CaptureBy :: Ref , euv:: Move ) | ( hir:: CaptureBy :: Value , euv:: Move | euv:: Copy ) => { }
1128
+ } ;
1129
+
1130
+ let place_with_id = PlaceWithHirId { place : place. clone ( ) , hir_id : place_with_id. hir_id } ;
1131
+
1132
+ if !self . capture_information . contains_key ( & place) {
1133
+ self . init_capture_info_for_place ( & place_with_id, diag_expr_id) ;
1118
1134
}
1119
1135
1120
1136
let tcx = self . fcx . tcx ;
@@ -1128,13 +1144,15 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
1128
1144
1129
1145
let usage_span = tcx. hir ( ) . span ( diag_expr_id) ;
1130
1146
1131
- // To move out of an upvar, this must be a FnOnce closure
1132
- self . adjust_closure_kind (
1133
- upvar_id. closure_expr_id ,
1134
- ty:: ClosureKind :: FnOnce ,
1135
- usage_span,
1136
- place_with_id. place . clone ( ) ,
1137
- ) ;
1147
+ if matches ! ( mode, euv:: Move ) {
1148
+ // To move out of an upvar, this must be a FnOnce closure
1149
+ self . adjust_closure_kind (
1150
+ upvar_id. closure_expr_id ,
1151
+ ty:: ClosureKind :: FnOnce ,
1152
+ usage_span,
1153
+ place. clone ( ) ,
1154
+ ) ;
1155
+ }
1138
1156
1139
1157
let capture_info = ty:: CaptureInfo {
1140
1158
capture_kind_expr_id : Some ( diag_expr_id) ,
@@ -1317,8 +1335,12 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
1317
1335
if let PlaceBase :: Upvar ( upvar_id) = place_with_id. place . base {
1318
1336
assert_eq ! ( self . closure_def_id. expect_local( ) , upvar_id. closure_expr_id) ;
1319
1337
1320
- let capture_kind =
1321
- self . fcx . init_capture_kind ( self . capture_clause , upvar_id, self . closure_span ) ;
1338
+ let capture_kind = self . fcx . init_capture_kind_for_place (
1339
+ & place_with_id. place ,
1340
+ self . capture_clause ,
1341
+ upvar_id,
1342
+ self . closure_span ,
1343
+ ) ;
1322
1344
1323
1345
let expr_id = Some ( diag_expr_id) ;
1324
1346
let capture_info = ty:: CaptureInfo {
@@ -1392,15 +1414,10 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
1392
1414
}
1393
1415
1394
1416
/// Truncate projections so that following rules are obeyed by the captured `place`:
1395
- ///
1396
- /// - No Derefs in move closure, this will result in value behind a reference getting moved.
1397
1417
/// - No projections are applied to raw pointers, since these require unsafe blocks. We capture
1398
1418
/// them completely.
1399
1419
/// - No Index projections are captured, since arrays are captured completely.
1400
- fn restrict_capture_precision < ' tcx > (
1401
- mut place : Place < ' tcx > ,
1402
- capture_kind : ty:: UpvarCapture < ' tcx > ,
1403
- ) -> Place < ' tcx > {
1420
+ fn restrict_capture_precision < ' tcx > ( mut place : Place < ' tcx > ) -> Place < ' tcx > {
1404
1421
if place. projections . is_empty ( ) {
1405
1422
// Nothing to do here
1406
1423
return place;
@@ -1412,7 +1429,6 @@ fn restrict_capture_precision<'tcx>(
1412
1429
}
1413
1430
1414
1431
let mut truncated_length = usize:: MAX ;
1415
- let mut first_deref_projection = usize:: MAX ;
1416
1432
1417
1433
for ( i, proj) in place. projections . iter ( ) . enumerate ( ) {
1418
1434
if proj. ty . is_unsafe_ptr ( ) {
@@ -1426,31 +1442,36 @@ fn restrict_capture_precision<'tcx>(
1426
1442
truncated_length = truncated_length. min ( i) ;
1427
1443
break ;
1428
1444
}
1429
- ProjectionKind :: Deref => {
1430
- // We only drop Derefs in case of move closures
1431
- // There might be an index projection or raw ptr ahead, so we don't stop here.
1432
- first_deref_projection = first_deref_projection. min ( i) ;
1433
- }
1445
+ ProjectionKind :: Deref => { }
1434
1446
ProjectionKind :: Field ( ..) => { } // ignore
1435
1447
ProjectionKind :: Subslice => { } // We never capture this
1436
1448
}
1437
1449
}
1438
1450
1439
- let length = place
1440
- . projections
1441
- . len ( )
1442
- . min ( truncated_length)
1443
- // In case of capture `ByValue` we want to not capture derefs
1444
- . min ( match capture_kind {
1445
- ty:: UpvarCapture :: ByValue ( ..) => first_deref_projection,
1446
- ty:: UpvarCapture :: ByRef ( ..) => usize:: MAX ,
1447
- } ) ;
1451
+ let length = place. projections . len ( ) . min ( truncated_length) ;
1448
1452
1449
1453
place. projections . truncate ( length) ;
1450
1454
1451
1455
place
1452
1456
}
1453
1457
1458
+ /// Truncates a place so that the resultant capture doesn't move data out of a reference
1459
+ fn truncate_capture_for_move ( mut place : Place < ' tcx > ) -> Place < ' tcx > {
1460
+ for ( i, proj) in place. projections . iter ( ) . enumerate ( ) {
1461
+ match proj. kind {
1462
+ ProjectionKind :: Deref => {
1463
+ // We only drop Derefs in case of move closures
1464
+ // There might be an index projection or raw ptr ahead, so we don't stop here.
1465
+ place. projections . truncate ( i) ;
1466
+ return place;
1467
+ }
1468
+ _ => { }
1469
+ }
1470
+ }
1471
+
1472
+ place
1473
+ }
1474
+
1454
1475
fn construct_place_string ( tcx : TyCtxt < ' _ > , place : & Place < ' tcx > ) -> String {
1455
1476
let variable_name = match place. base {
1456
1477
PlaceBase :: Upvar ( upvar_id) => var_name ( tcx, upvar_id. var_path . hir_id ) . to_string ( ) ,
0 commit comments