@@ -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 ,
@@ -205,11 +206,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
205
206
// If we have an origin, store it.
206
207
if let Some ( origin) = delegate. current_origin . clone ( ) {
207
208
let origin = if self . tcx . features ( ) . capture_disjoint_fields {
208
- origin
209
+ ( origin. 0 , restrict_capture_precision ( origin . 1 ) )
209
210
} else {
210
- // FIXME(project-rfc-2229#31): Once the changes to support reborrowing are
211
- // made, make sure we are selecting and restricting
212
- // the origin correctly.
213
211
( origin. 0 , Place { projections : vec ! [ ] , ..origin. 1 } )
214
212
} ;
215
213
@@ -449,7 +447,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
449
447
base => bug ! ( "Expected upvar, found={:?}" , base) ,
450
448
} ;
451
449
452
- let place = restrict_capture_precision ( place, capture_info . capture_kind ) ;
450
+ let place = restrict_capture_precision ( place) ;
453
451
454
452
let min_cap_list = match root_var_min_capture_list. get_mut ( & var_hir_id) {
455
453
None => {
@@ -897,15 +895,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
897
895
}
898
896
}
899
897
900
- fn init_capture_kind (
898
+ fn init_capture_kind_for_place (
901
899
& self ,
900
+ place : & Place < ' tcx > ,
902
901
capture_clause : hir:: CaptureBy ,
903
902
upvar_id : ty:: UpvarId ,
904
903
closure_span : Span ,
905
904
) -> ty:: UpvarCapture < ' tcx > {
906
905
match capture_clause {
907
- hir:: CaptureBy :: Value => ty:: UpvarCapture :: ByValue ( None ) ,
908
- hir:: CaptureBy :: Ref => {
906
+ // In case of a move closure if the data is accessed through a reference we
907
+ // want to capture by ref to allow precise capture using reborrows.
908
+ //
909
+ // If the data will be moved out of this place, then the place will be truncated
910
+ // at the first Deref in `adjust_upvar_borrow_kind_for_consume` and then moved into
911
+ // the closure.
912
+ hir:: CaptureBy :: Value if !place. deref_tys ( ) . any ( ty:: TyS :: is_ref) => {
913
+ ty:: UpvarCapture :: ByValue ( None )
914
+ }
915
+ hir:: CaptureBy :: Value | hir:: CaptureBy :: Ref => {
909
916
let origin = UpvarRegion ( upvar_id, closure_span) ;
910
917
let upvar_region = self . next_region_var ( origin) ;
911
918
let upvar_borrow = ty:: UpvarBorrow { kind : ty:: ImmBorrow , region : upvar_region } ;
@@ -1109,12 +1116,25 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
1109
1116
place_with_id, diag_expr_id, mode
1110
1117
) ;
1111
1118
1112
- // we only care about moves
1113
- match mode {
1114
- euv:: Copy => {
1119
+ match ( self . capture_clause , mode) {
1120
+ // In non-move closures, we only care about moves
1121
+ ( hir:: CaptureBy :: Ref , euv:: Copy ) => return ,
1122
+
1123
+ // We want to capture Copy types that read through a ref via a reborrow
1124
+ ( hir:: CaptureBy :: Value , euv:: Copy )
1125
+ if place_with_id. place . deref_tys ( ) . any ( ty:: TyS :: is_ref) =>
1126
+ {
1115
1127
return ;
1116
1128
}
1117
- euv:: Move => { }
1129
+
1130
+ ( hir:: CaptureBy :: Ref , euv:: Move ) | ( hir:: CaptureBy :: Value , euv:: Move | euv:: Copy ) => { }
1131
+ } ;
1132
+
1133
+ let place = truncate_capture_for_move ( place_with_id. place . clone ( ) ) ;
1134
+ let place_with_id = PlaceWithHirId { place : place. clone ( ) , hir_id : place_with_id. hir_id } ;
1135
+
1136
+ if !self . capture_information . contains_key ( & place) {
1137
+ self . init_capture_info_for_place ( & place_with_id, diag_expr_id) ;
1118
1138
}
1119
1139
1120
1140
let tcx = self . fcx . tcx ;
@@ -1128,13 +1148,15 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
1128
1148
1129
1149
let usage_span = tcx. hir ( ) . span ( diag_expr_id) ;
1130
1150
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
- ) ;
1151
+ if matches ! ( mode, euv:: Move ) {
1152
+ // To move out of an upvar, this must be a FnOnce closure
1153
+ self . adjust_closure_kind (
1154
+ upvar_id. closure_expr_id ,
1155
+ ty:: ClosureKind :: FnOnce ,
1156
+ usage_span,
1157
+ place. clone ( ) ,
1158
+ ) ;
1159
+ }
1138
1160
1139
1161
let capture_info = ty:: CaptureInfo {
1140
1162
capture_kind_expr_id : Some ( diag_expr_id) ,
@@ -1317,8 +1339,12 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
1317
1339
if let PlaceBase :: Upvar ( upvar_id) = place_with_id. place . base {
1318
1340
assert_eq ! ( self . closure_def_id. expect_local( ) , upvar_id. closure_expr_id) ;
1319
1341
1320
- let capture_kind =
1321
- self . fcx . init_capture_kind ( self . capture_clause , upvar_id, self . closure_span ) ;
1342
+ let capture_kind = self . fcx . init_capture_kind_for_place (
1343
+ & place_with_id. place ,
1344
+ self . capture_clause ,
1345
+ upvar_id,
1346
+ self . closure_span ,
1347
+ ) ;
1322
1348
1323
1349
let expr_id = Some ( diag_expr_id) ;
1324
1350
let capture_info = ty:: CaptureInfo {
@@ -1392,15 +1418,10 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
1392
1418
}
1393
1419
1394
1420
/// 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
1421
/// - No projections are applied to raw pointers, since these require unsafe blocks. We capture
1398
1422
/// them completely.
1399
1423
/// - 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 > {
1424
+ fn restrict_capture_precision < ' tcx > ( mut place : Place < ' tcx > ) -> Place < ' tcx > {
1404
1425
if place. projections . is_empty ( ) {
1405
1426
// Nothing to do here
1406
1427
return place;
@@ -1412,7 +1433,6 @@ fn restrict_capture_precision<'tcx>(
1412
1433
}
1413
1434
1414
1435
let mut truncated_length = usize:: MAX ;
1415
- let mut first_deref_projection = usize:: MAX ;
1416
1436
1417
1437
for ( i, proj) in place. projections . iter ( ) . enumerate ( ) {
1418
1438
if proj. ty . is_unsafe_ptr ( ) {
@@ -1426,31 +1446,30 @@ fn restrict_capture_precision<'tcx>(
1426
1446
truncated_length = truncated_length. min ( i) ;
1427
1447
break ;
1428
1448
}
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
- }
1449
+ ProjectionKind :: Deref => { }
1434
1450
ProjectionKind :: Field ( ..) => { } // ignore
1435
1451
ProjectionKind :: Subslice => { } // We never capture this
1436
1452
}
1437
1453
}
1438
1454
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
- } ) ;
1455
+ let length = place. projections . len ( ) . min ( truncated_length) ;
1448
1456
1449
1457
place. projections . truncate ( length) ;
1450
1458
1451
1459
place
1452
1460
}
1453
1461
1462
+ /// Truncates a place so that the resultant capture doesn't move data out of a reference
1463
+ fn truncate_capture_for_move ( mut place : Place < ' tcx > ) -> Place < ' tcx > {
1464
+ if let Some ( i) = place. projections . iter ( ) . position ( |proj| proj. kind == ProjectionKind :: Deref ) {
1465
+ // We only drop Derefs in case of move closures
1466
+ // There might be an index projection or raw ptr ahead, so we don't stop here.
1467
+ place. projections . truncate ( i) ;
1468
+ }
1469
+
1470
+ place
1471
+ }
1472
+
1454
1473
fn construct_place_string ( tcx : TyCtxt < ' _ > , place : & Place < ' tcx > ) -> String {
1455
1474
let variable_name = match place. base {
1456
1475
PlaceBase :: Upvar ( upvar_id) => var_name ( tcx, upvar_id. var_path . hir_id ) . to_string ( ) ,
0 commit comments